Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将在UI线程上运行的代码移动并访问线程的全局状态_C#_.net_Multithreading_Windows Phone 7 - Fatal编程技术网

C# 将在UI线程上运行的代码移动并访问线程的全局状态

C# 将在UI线程上运行的代码移动并访问线程的全局状态,c#,.net,multithreading,windows-phone-7,C#,.net,Multithreading,Windows Phone 7,我正试图在我的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 =

我正试图在我的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线程的自由


谢谢

有几种方法

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

或者,您可以让一个线程始终运行,专用于
处理
线程。使用a在UI线程和
处理
线程之间进行通信。每当单击按钮时,将相关信息放在BlockingCollection上,并让
处理线程从该BlockingCollection中拉出工作项

应接近正常的未测试代码:

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;
}
试试这个:

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;
}

一个非常简单的解决方案是使用。它允许您将工作卸载到后台线程,并在完成时通知您。(另一个选项见下文)

另一个选择是为下一版本的Windows Phone准备好代码,并开始使用任务并行库。TPL可用于.Net4,但不可用于Windows Phone。有一些确实支持Silverlight和Windows Phone。将其中一个包添加到项目中,您可以将代码更改为(语法可能不是100%正确):

private Task ProcessAsync(inta、intb、intc)
{
var taskCompletionSource=新的taskCompletionSource();
var task=task.Factory.StartNew(()=>
{
//做你的工作
返回newInt;
}
task.ContinueWith(t=>taskCompletionSource.SetResult(t.Result));
返回taskCompletionSource.Task;
}
无效按钮\单击(对象发送者、事件参数)
{
//禁用该按钮以防止再次单击
MyButton.IsEnabled=false;
var task=ProcessAsync(1,2,3);
task.ContinueWith(t=>
{
MyTextBox.Content=t.Result;
MyButton.IsEnabled=true;
});
}

如果用户“单击”的速度快于处理完成工作的速度,该怎么办?您需要在启动后台线程之前禁用您的按钮。button.Enabled=false。在设置MyTextBox.Content的地方,您可以重新启用它。我更新了代码。虽然这提高了用户体验(这是一件好事),我不会依赖UI状态来保证后台工作人员的线程安全。有一天,有人会添加一个菜单来完成与按钮今天所做的相同的事情。单独关注。让UI代码负责管理向用户的演示,处理代码负责以线程安全的方式完成其工作。然后使用控制器,MVC或MVP超出了这个问题的范围。我不是说MVC。我是说对线程代码进行编程,使其无论UI代码做什么都是线程安全的。很好的解决方案!但Windows Phone没有BlockingCollection.:(这太糟糕了!但是,在.NET中使用较旧的线程结构实现生产者/消费者队列非常简单。这里有详细的演练,或者这里有一个更灵活的版本,我推荐整个关于线程的电子书(我的参考资料)很好的示例。因此,我可以使用ContinueWith从非等待函数(如事件)调用异步?您可以使用任何一种方法。事件可以是异步方法,也可以等待调用。您只需要小心b/c现在您有一个异步void方法,这很危险。请确保始终将等待调用包装在try/catch中。请记住,这个答案已经有近1年半的历史了,所以,是否有一些改进。我知道,我不能期望事件等待async(例如,如果我在ApplicationLaunching()中有一些async init,phone将不会等待)。但是如果我有ProcessAsync和ContinueWith,并且我在其中调用rising new event,或者隐藏splashscreen,那么应该可以吗?
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;
}
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();
}
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;
        });
}