我感興趣的是爲什麼我們需要調用InvokeOnMainThread,而這將是TaskScheduler.FromCurrentSynchronizationContext()的主要意圖和責任。爲什麼不在Monotouch中同步TaskScheduler.FromCurrentSynchronizationContext?
我正在Monotouch中爲iPhone應用程序使用TPL來完成一些後臺任務並通過記者級更新UI。但是,似乎TaskScheduler.FromCurrentSynchronizationContext()並不像您所期望的那樣與UI線程同步。此時,我通過使用Xamarin站點中Threading主題描述的InvokeOnMainThread,設法使其工作(但仍然感覺錯誤)。
我還發現在BugZilla上報告(類似)bug,似乎已解決..和另一個threading question關於在MonoTouch中使用後臺線程的首選方法。
以下是用於說明我的問題和顯示行爲的代碼片段。
private CancellationTokenSource cancellationTokenSource;
private void StartBackgroundTask()
{
this.cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = this.cancellationTokenSource.Token;
var progressReporter = new ProgressReporter();
int n = 100;
var uiThreadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine ("Start in thread " + uiThreadId);
var task = Task.Factory.StartNew (() =>
{
for (int i = 0; i != n; ++i) {
Console.WriteLine ("Work in thread " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep (30);
progressReporter.ReportProgress (() =>
{
Console.WriteLine ("Reporting in thread {0} (should be {1})",
Thread.CurrentThread.ManagedThreadId,
uiThreadId);
this.progressBar.Progress = (float)(i + 1)/n;
this.progressLabel.Text = this.progressBar.Progress.ToString();
});
}
return 42; // Just a mock result
}, cancellationToken);
progressReporter.RegisterContinuation (task,() =>
{
Console.WriteLine ("Result in thread {0} (should be {1})",
Thread.CurrentThread.ManagedThreadId,
uiThreadId);
this.progressBar.Progress = (float)1;
this.progressLabel.Text = string.Empty;
Util.DisplayMessage ("Result","Background task result: " + task.Result);
});
}
而記者類有這些方法
public void ReportProgress(Action action)
{
this.ReportProgressAsync(action).Wait();
}
public Task ReportProgressAsync(Action action)
{
return Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
public Task RegisterContinuation(Task task, Action action)
{
return task.ContinueWith(() => action(), CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
public Task RegisterContinuation<TResult>(Task<TResult> task, Action action)
{
return task.ContinueWith(() => action(), CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
在應用程序的輸出窗口中的結果將是:
Start in thread 1
Work in thread 6
Reporting in thread 6 (should be 1)
Work in thread 6
Reporting in thread 6 (should be 1)
...
Result in thread 1 (should be 1)
正如你可以看到「螺紋6工作」是罰款。報告也在線索6上,這是錯誤的。有趣的是,RegisterContinuation
在線程1中進行了報告!
進展:我還沒有想出這一個出來..任何人?
謝謝桑德爾,但我試過這個,它沒有工作。它在控制檯輸出中給出相同的結果,UI中沒有顯示進度。正如我的問題所述,'ContinueWith()'似乎無需調用'InvokeOnMainThread()'即可工作。 – 2012-03-21 00:50:27
我不得不承認,我僅在Windows上使用WinForms項目嘗試了此操作。在Windows上,控制檯項目甚至不允許使用TaskScheduler.FromCurrentSynchronizationContext(),它會導致引發異常:「當前的SynchronizationContext不能用作TaskScheduler。」 – 2012-03-21 08:51:12
@RemcoKoedoot,你能否展示你使用這種技術的更新代碼(作爲對問題的編輯)? – 2013-05-10 21:27:22