1

我正在尝试将尽可能多的处理移出我的 Windows Phone 应用程序的 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;
}

如果处理方法没有设置/获取全局状态变量,那么在线程上移动该代码的执行将非常容易。

如何确保一次只有一个线程以正确的顺序运行?现在很容易,因为处理代码在 UI 线程上运行。UI 线程的好处是它可以保证一切都以正确的顺序运行,并且一次一个。我如何用线程复制它?

我可以将整个代码重构为几乎没有全局状态,但现在不一定要这样做。我也可以使用锁,但我只是想知道是否有更好的方法。我正在做的处理不是很重。但是,有时我会在 UI 中看到一些滞后,我希望尽可能保持 UI 线程自由。

谢谢!

4

3 回答 3

1

有几种方法。

如果您打算为每个 Button_Click 事件启动一个新线程,那么实际上您可能有多个线程希望写入相同的变量。lock您可以通过在语句中包装对这些变量的访问来解决这个问题。

或者,您可以让一个线程始终专用于该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 回答
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();
}

另一种选择是让您的代码为下一版本的 Windows Phone 做好准备并开始使用任务并行库。TPL 适用于 .Net4,但不适用于 Windows Phone。有一些NuGet 包确实支持 Silverlight 和 Windows Phone。将这些包之一添加到您的项目中,您可以将代码更改为(语法可能不是 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 回答
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 回答