2

私が興味を持っているのは、これが 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 で報告を行うことです!!!


進行状況: 私はまだこれを理解していません.. 誰かいますか?

4

2 に答える 2

1

問題は、を実行して ProgressReporter クラス内からタスク スケジューラを取得していることだと思いますTaskScheduler.FromCurrentSynchronizationContext()

タスク スケジューラを ProgressReporter に渡し、代わりにそれを使用する必要があります。

public class ProgressReporter
{
    private readonly TaskScheduler taskScheduler;

    public ProgressReporter(TaskScheduler taskScheduler)
    {
        this.taskScheduler = taskScheduler;
    }

    public Task RegisterContinuation(Task task, Action action)
    {
        return task.ContinueWith(n => action(), CancellationToken.None,
            TaskContinuationOptions.None, taskScheduler);
    }

    // Remaining members...
}

UI スレッドから取得したタスク スケジューラを進行状況レポーターに渡すことで、UI スレッドでレポートが確実に実行されます。

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
ProgressReporter progressReporter = new ProgressReporter(uiScheduler);
于 2012-03-20T20:22:57.167 に答える
0

使用している MonoTouch のバージョンと、次の出力は何ですか: TaskScheduler.FromCurrentSynchronizationContext ().GetType ().ToString (). コンテキストが正しく登録されている場合、UIKitSynchronizationContext 型のクラスである必要があります。これが正しいタイプのコンテキストである場合、コンテキストで Post および Send メソッドを直接呼び出して、正しいスレッドで実行されるかどうかを確認する簡単なテストを行うことができます。正しく動作することをテストするには、いくつかのスレッドプール スレッドを起動する必要がありますが、かなり単純なはずです。

于 2012-03-21T01:59:28.947 に答える