1

WindowsPhoneアプリのUIスレッドからできるだけ多くの処理を移動しようとしています。ボタンをクリックすると実行されるコードがあります。このコードは、概念的には以下のコードに似ています。

private int Processing(int a, int b, int c) { 
   this.A = this.moreProcessing(a);
   this.B = this.moreProcessing(b);
   this.C = this.moreProcessing(c);

   int newInt = /* ... */
   return newInt;
}

public void Button_Click(object sender, EventArgs args) {
   var result = Processing(1, 2, 3);
   this.MyTextBox.Content = result;
}

Processingメソッドがグローバル状態変数を設定/取得していない場合、スレッド上のそのコードの実行を移動するのは非常に簡単です。

一度に1つのスレッドだけが正しい順序で実行されていることを確認するにはどうすればよいですか?現在、処理コードはUIスレッドで実行されるため、簡単です。UIスレッドの良いところは、すべてが正しい順序で一度に1つずつ実行されることを保証することです。スレッドでそれを複製するにはどうすればよいですか?

コード全体をリファクタリングしてグローバル状態をほとんど持たないようにすることはできますが、現時点で必ずしもそうできるとは限りません。ロックを使用することもできますが、もっと良い方法があるかどうか疑問に思っています。私が行っている処理はそれほど重くはありません。ただし、UIに多少の遅れが見られることがあるので、UIスレッドをできるだけ自由に保ちたいと思います。

ありがとう!

4

3 に答える 3

1

非常に簡単な解決策は、BackgroundWorkerを使用することです。作業をバックグラウンド スレッドにオフロードし、完了時に通知することができます。(別のオプションについては以下を参照してください)

void Button_Click(object sender, EventArgs args)
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += (s, e) =>
                     {
                         e.Result = Processing(1, 2, 3);
                     };
    worker.RunWorkerCompleted += (s1, e1) =>
                                 {
                                     MyTextBox.Content = e1.Result;
                                     MyButton.IsEnabled = true;
                                 };

    // Disable the button to stop multiple clicks
    MyButton.IsEnabled = false;
    worker.RunWorkerAsync();
}

もう 1 つのオプションは、次のバージョンの Windows Phone に対応するようにコードを準備し、Task Parallel Library の使用を開始することです。TPL は .Net4 で使用できますが、Windows Phone では使用できません。Silverlight と Windows Phone をサポートするNuGet パッケージがいくつかあります。これらのパッケージのいずれかをプロジェクトに追加すると、コードを次のように変更できます (構文は 100% 正しくない場合があります)。

private Task<int> ProcessAsync(int a, int b, int c)
{
    var taskCompletionSource = new TaskCompletionSource<int>();

    var task = Task.Factory.StartNew<int>(() =>
    {
        // Do your work

        return newInt;
    }
    task.ContinueWith(t => taskCompletionSource.SetResult(t.Result));
    return taskCompletionSource.Task;
}

void Button_Click(object sender, EventArgs args)
{
    // Disable the button to prevent more clicks
    MyButton.IsEnabled = false;

    var task = ProcessAsync(1,2,3);
    task.ContinueWith(t => 
        {
             MyTextBox.Content = t.Result;
             MyButton.IsEnabled = true;
        });
}
于 2012-06-28T17:54:00.860 に答える
1

いくつかのアプローチがあります。

Button_Click イベントごとに新しいスレッドを起動する場合は、同じ変数に書き込みたい複数のスレッドを持つことができます。これらの変数へのアクセスをlockステートメントでラップすることで、これを解決できます。

または、スレッド専用の 1 つのスレッドを常に実行することもできProcessingます。BlockingCollectionを使用して、UI スレッドとスレッドの間で通信しProcessingます。Button_Click が発生するたびに、関連情報を BlockingCollection に配置し、Processingスレッドにその BlockingCollection から作業項目をプルさせます。

OK に近いはずのテストされていないコード:

class ProcessingParams // Or use a Tuple<int, int, int>
{
    public int A { get; set; }
    public int B { get; set; }
    public int C { get; set; }
}

BlockingCollection<int> bc = new BlockingCollection<int>();

private int Processing() { 

    try
    {
        while (true) 
        {
            ProcesingParams params = bc.Take();         
           this.A = this.moreProcessing(params.A);
           this.B = this.moreProcessing(params.B);
           this.C = this.moreProcessing(params.C);

           int newInt = /* ... */
           return newInt; // Rather than 'return' the int,  place it in this.MyTextBox.Content using thread marshalling
        }
    }
    catch (InvalidOperationException)
    {
        // IOE means that Take() was called on a completed collection
    }
}

public void Button_Click(object sender, EventArgs args) {
   //var result = Processing(1, 2, 3);
   bc.Add (new ProcessingParams() { A = 1, B = 2, C = 3 };
   //this.MyTextBox.Content = result;
}

アプリケーションが終了したら、忘れずに電話してください

bc.CompleteAdding(); // Causes the processing thread to end
于 2012-06-28T17:37:01.023 に答える
0

これを試して:

public void Button_Click(object sender, EventArgs args)
{
    Button.Enabled = false;

    ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundProcessing));
}

private void BackgroundProcessing(object state)
{
    var result = Processing(1, 2, 3);

    // Call back to UI thread with results
    Invoke(new Action(() => { 
        this.MyTextBox.Content = result;
        Button.Enabled = true;
     }));
}

private int Processing(int a, int b, int c)
{ 
   this.A = this.moreProcessing(a);
   this.B = this.moreProcessing(b);
   this.C = this.moreProcessing(c);

   int newInt = /* ... */
   return newInt;
}
于 2012-06-28T17:39:13.273 に答える