私が興味を持っているのは、InvokeOnMainThreadを呼び出す必要があるのですが、これはTaskScheduler.FromCurrentSynchronizationContext()の主な目的と責任ですか?なぜTaskScheduler.FromCurrentSynchronizationContextはMonotouchで同期しませんか?
iPhoneアプリのMonotouchでTPLを使用して、いくつかのバックグラウンドタスクを実行し、レポータークラスを介してUIを更新しています。しかし、TaskScheduler.FromCurrentSynchronizationContext()はUIスレッドと期待通りに同期していないようです。現時点では、XamarinのサイトのThreadingトピックで説明されているように、InvokeOnMainThreadを使用することで問題なく動作します(ただし、間違っていると感じています)。
また、BugZillaで報告された(類似した)bugが解決されているように見え、MonoTouchで背景スレッドを使用する好ましい方法についてもう1つのthreading questionが見つかりました。
私の質問を説明し、その動作を示すためのコードスニペットを以下に示します。
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()'を呼び出す必要なく動作するようです。 –
私はWinFormsプロジェクトを使用してWindowsでのみ試したことを認めなければなりません。 Windowsでは、コンソールプロジェクトは 'TaskScheduler.FromCurrentSynchronizationContext()'を許可しません。例外がスローされます: '現在のSynchronizationContextはTaskSchedulerとして使用できません。' –
@RemcoKoedoot、このテクニックを使用する最新のコードを(質問の編集として)表示できますか? –