Windows Metro 앱을 개발 중이며 UI가 응답하지 않는 문제가 발생합니다. 내가 알 수있는 한 그 원인은 다음과 같습니다.
<ListView
...
SelectionChanged="ItemListView_SelectionChanged"
...
이 이벤트는 여기에서 처리됩니다.
async void ItemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.UsingLogicalPageNavigation()) this.InvalidateVisualState();
MyDataItem dataItem = e.AddedItems[0] as MyDataItem;
await LoadMyPage(dataItem);
}
private async Task LoadMyPage(MyDataItem dataItem)
{
SyndicationClient client = new SyndicationClient();
SyndicationFeed feed = await client.RetrieveFeedAsync(new Uri(FEED_URI));
string html = ConvertRSSToHtml(feed)
myWebView.NavigateToString(html, true);
}
LoadMyPage
웹 서비스에서 데이터를 가져 와서 화면에로드 할 때 완료하는 데 시간이 걸립니다. 그러나 UI가 기다리고있는 것처럼 보입니다. 내 생각 엔 위 이벤트가 완료 될 때까지 기다리는 것입니다.
그래서 제 질문은 이것에 대해 어떻게해야합니까? 내가 연결할 수있는 더 좋은 이벤트가 있습니까, 아니면 이것을 처리 할 다른 방법이 있습니까? 나는 배경 작업을 시작하는 것에 대해 생각했지만, 그것은 나에게 잔인한 것처럼 보입니다.
편집하다:
이 문제의 규모를 명확히하기 위해 최대 3 ~ 4 초가 응답하지 않습니다. 이것은 결코 장기간에 걸친 작업이 아닙니다.
편집하다:
아래의 제안을 시도했지만, 전체 호출 스택은SelectionChanged
함수가 async / await를 사용 중입니다. 나는이 진술을 추적했다.
myFeed = await client.RetrieveFeedAsync(uri);
완료 될 때까지 처리를 계속하지 않는 것 같습니다.
편집하다:
나는 이것이 War & 평화이지만 아래는 빈 대도시 앱과 버튼을 사용하여 문제를 복제 한 것입니다.
XAML :
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<Button Click="Button_Click_1" Width="200" Height="200">test</Button>
<TextBlock x:Name="test"/>
</StackPanel>
</Grid>
코드 숨김 :
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
SyndicationFeed feed = null;
SyndicationClient client = new SyndicationClient();
Uri feedUri = new Uri(myUri);
try
{
feed = await client.RetrieveFeedAsync(feedUri);
foreach (var item in feed.Items)
{
test.Text += item.Summary.Text + Environment.NewLine;
}
}
catch
{
test.Text += "Connection failed\n";
}
}
이것을 시도해라. ..
SyndicationFeed feed = null;
SyndicationClient client = new SyndicationClient();
var feedUri = new Uri(myUri);
try {
var task = client.RetrieveFeedAsync(feedUri).AsTask();
task.ContinueWith((x) => {
var result = x.Result;
Parallel.ForEach(result.Items, item => {
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
{
test.Text += item.Title.Text;
});
});
});
}
catch (Exception ex) { }
Grid 앱 템플릿을 사용하여 앱에 버튼을 추가하여 내 컴퓨터에서이를 시도했습니다. 내가 문제없이 페이지의 제목을 업데이트하는 동안 항목의 표를 앞뒤로 스크롤 할 수있었습니다. 내가 아이템을 많이 갖고 있지는 않았지만 정말 빨랐다. 그래서 100 % 긍정적 인 것은 힘들었다.
ForEach
) - 감사. 나는 그 인상을 받고 있었다.await
효과적으로 이것과 같았습니다 - 그래서 이것이 무엇입니까?await
그렇지 않습니다. - pm_2
당신이 사용하고 있기 때문에await
앞에LoadMyPage
나는 그것을 컴파일한다고 가정하고 리턴한다.Task
. 그렇다면, 나는 약간의 예를 만들었습니다.
우리는LoadMyPage
(과Sleep()
) 다음과 같이 보입니다.
public Task<string> LoadMyPage()
{
return Task<string>.Factory.StartNew(() =>
{
Sleep(3000);
return "Hello world";
});
}
static void Sleep(int ms)
{
new ManualResetEvent(false).WaitOne(ms);
}
그리고 그XAML
다음과 같이 보입니다.
<StackPanel>
<TextBlock x:Name="Result" />
<ListView x:Name="MyList" SelectionChanged="ItemListView_SelectionChanged">
<ListViewItem>Test</ListViewItem>
<ListViewItem>Test2</ListViewItem>
</ListView>
<Button>Some Button</Button>
<Button>Some Button2</Button>
</StackPanel>
그런 다음SelectionChanged
다음과 같은 이벤트 핸들러 :
private async void ItemListView_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
MyList.IsEnabled = false;
var result = await LoadMyPage();
Result.Text = result;
MyList.IsEnabled = true;
}
그만큼Task
그LoadMyPage
반환은 병렬로 실행됩니다. 즉, 해당 작업이 실행 중일 때UI
얼지 않아야 해. 그 결과를 얻으려면Task
너는 사용한다await
. 이렇게하면 연속 블록이 생성됩니다.
따라서이 예에서 무언가를 선택하면ListView
전체로드 시간 동안 사용 중지 된 다음 다시 활성화됩니다.Task
끝내다. 버튼이 여전히 반응적임을 알기 위해 버튼을 눌러 UI가 멈추지 않았 음을 확인할 수 있습니다.
만약LoadMyPage
UI와 상호 작용할 때, 약간의 재배치가 필요하다.ViewModel
또는 원하는 결과를 UI 스레드에 다시 함께 넣을 수 있습니다.
RetrieveFeedAsync
- pm_2Sleep
사용하여ManualResetEvent(false).WaitOne(ms)
. 내 대답이 어떤 것인지 모르겠다.async
, 분명히 할 수 있겠 니? - Filip EkbergTask
비동기 적으로 실행되며awaited
...에서ItemListView_SelectionChanged
. - Filip Ekberg
백그라운드 스레드가 가장 확실합니다.아니과잉. 이것이 바로 당신이 이런 종류의 문제를 어떻게 다루는가입니다.
UI 스레드에서 오랜 시간 작업을 수행하지 마십시오. 그렇지 않으면 UI가 묶여 응답하지 않게됩니다. 백그라운드 스레드에서 실행 한 다음 해당 스레드가 완료되면 기본 UI 스레드에서 처리 할 수있는 이벤트를 발생 시키십시오.
UI 스레드에 어떤 종류의 진행 표시기를 표시하는 것도 유용합니다. 사용자는무언가 일어나고있다.. 앱이 깨지거나 얼지 않는다는 것을 확인시켜 주면 조금 더 기다릴 수 있습니다. 이런 이유로 모든 웹 브라우저에는 어떤 종류의 "throbber"또는 다른로드 표시기가 있습니다.
가장 큰 문제는LoadMyPage
동 기적으로 뭔가를하고 있습니다. 생각해 내다,async
백그라운드 스레드에서 코드를 실행하지 않습니다. 기본적으로 모든 실제암호UI 스레드에서 실행됩니다 (비동기 / 대기 FAQ또는 내비동기 / 기다리기 소개). 따라서 비동기 메서드에서 차단하면 여전히 호출 스레드를 차단하고 있습니다.
보세요LoadMyPage
. 사용하고 있습니까?await
웹 서비스를 호출하려면? 데이터를 UI에 넣기 전에 값 비싼 처리를하고 있습니까? UI를 압도하고 있습니까 (많은 Windows 컨트롤은 수천 개의 요소에 도달하면 확장 성 문제가 있습니까?)
LoadMyPage
전화 걸기await Task.Run(() => {}).ConfigureAwait(false)
전화하기 전에RetrieveFeedAsync
. 무슨 일이야? - Stephen Cleary
귀하의 단순화 된 코드 예제를 보면, 나는 당신의 문제가외부기다리고있는 줄.
다음 코드 블록에서 :
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
SyndicationFeed feed = null;
SyndicationClient client = new SyndicationClient();
Uri feedUri = new Uri(myUri);
try
{
feed = await client.RetrieveFeedAsync(feedUri);
foreach (var item in feed.Items)
{
test.Text += item.Summary.Text + Environment.NewLine;
}
}
catch
{
test.Text += "Connection failed\n";
}
}
그만큼만백그라운드 스레드에서 실행중인 행은 다음과 같습니다.
feed = await client.RetrieveFeedAsync(feedUri);
모든해당 블록의 다른 코드 행이 UI 스레드에서 실행 중입니다.
버튼 클릭 처리기가 비동기로 표시 되어도 UI 스레드에서 실행되지 않는 코드는 아닙니다. 실제로 이벤트 핸들러는 UI 스레드에서 시작됩니다. 따라서 SyndicationClient를 생성하고 Uri를 설정하는 것은 UI 스레드에서 발생합니다.
많은 개발자들이 깨닫지 못하는 부분은후사용중인 동일한 스레드에서 자동으로 다시 시작됩니다.전에기다리고있어. 이것은 코드를 의미합니다.
foreach (var item in feed.Items)
{
test.Text += item.Summary.Text + Environment.NewLine;
}
UI 스레드에서 실행 중입니다!
이것은 Dispatcher.Invoke를 업데이트 할 필요가 없다는 점에서 편리합니다.test.Text, 또한 항목을 반복하고 문자열을 연결하는 동안 UI 스레드를 차단한다는 의미이기도합니다.
간단한 (비록 단순화 된) 예제에서 백그라운드 스레드에서이 작업을 수행하는 가장 쉬운 방법은 SyndicationClient에 다른 메소드를 호출하는 것입니다.RetrieveFeedAsStringAsync; 그런 다음 SyndicationClient는 자체 작업의 일부로 문자열의 다운로드, 반복 및 연결을 수행 할 수 있습니다. 작업이 완료된 후 UI 스레드에서 실행되는 유일한 코드 줄은 TextBox에 텍스트를 할당하는 것입니다.
foreach()
고리. - pm_2
foreach
모두 루프 - pm_2