私が興味を持っているのは、これが TaskScheduler.FromCurrentSynchronizationContext() の主な意図と責任であるのに、なぜ InvokeOnMainThread を呼び出す必要があるのかということです。
iPhone アプリの Monotouch で TPL を使用して、いくつかのバックグラウンド タスクを実行し、レポーター クラスを介して UI を更新しています。しかし、 TaskScheduler.FromCurrentSynchronizationContext() は、期待どおりに UI スレッドに同期していないようです。現時点では、Xamarin のサイトのThreadingトピックで説明されているように、InvokeOnMainThread を使用して動作させることができました (ただし、まだ間違っているように感じます)。
また、BugZilla で報告された (同様の)バグが見つかりましたが、これは解決されているようです.. と、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)
ご覧のとおり、「Work in thread 6」は問題ありません。スレッド 6 にも報告がありますが、これは誤りです。面白いのはRegisterContinuation
、スレッド 1 で報告を行うことです!!!
進行状況: 私はまだこれを理解していません.. 誰かいますか?