任务线程安全呼叫

时间:2016-03-25 06:04:17

标签: c#

我一直在使用system.threading一段时间,我试图绕过任务。如何使用TPL从另一个线程对UI控件(例如文本框)进行线程安全调用?

这是一个简单的例子,我希望用我的辅助线程计数每1秒更新一个文本框。

我尝试了一些不同的方法,但似乎无法让它发挥作用。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        await taskAsync();
    }

    private  Task taskAsync()
    {
        return Task.Factory.StartNew(() => counter());
    }

    private void counter()
    {
        for (int i = 0; i < 10000; i++)
        {
            Task.Delay(1000).Wait();
            textBox1.Text = i.ToString();
        }
    }
}

这甚至可能吗?

谢谢

3 个答案:

答案 0 :(得分:2)

在您当前的情况下,我认为Progress最合适。 我在下面的代码中做了一些修改:

public partial class Form1 : Form
{
    Progress<int> counterProgress;

    public Form1()
    {
        InitializeComponent();
        counterProgress = new Progress<int>();
        counterProgress.ProgressChanged += CounterProgressUpdated;
    }

    private void CounterProgressUpdated(object sender, int e)
    {
        textBox1.Text = e.ToString();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        await taskAsync(counterProgress);
    }

    private Task taskAsync(IProgress<int> progress)
    {
        return Task.Factory.StartNew(() => counter(progress));
    }

    private async Task counter(IProgress<int> progress)
    {
        for (int i = 0; i < 10000; i++)
        {
            await Task.Delay(1000);
            progress.Report(i);                
        }
    }
}

由于进度捕获了构造中的当前同步上下文,因此只要在UI线程上创建它就应该很好。

  

提供给使用ProgressChanged事件注册的构造函数或事件处理程序的任何处理程序都是通过构造实例时捕获的SynchronizationContext实例调用的。如果在构造时没有当前的SynchronizationContext,则将在ThreadPool上调用回调。

答案 1 :(得分:1)

这样的事情怎么样?

private void counter() {
    for (int i = 0; i < 10000; i++) {
        Thread.Sleep(1000); 
            // “There's never an advantage in replacing Thread.Sleep(1000); in Task.Delay(1000).Wait();”
            // http://stackoverflow.com/a/29357131/4267982

        Dispatcher.Invoke(() => { 
            textBox1.Text = i.ToString();
        });
    }
}

或者,作为一种选择,另一种没有单独线程的方式(所以不需要同步):

private async Task taskAsync() {
    for (int i = 0; i < 10000; i++) {
        await Task.Delay(1000);
        textBox1.Text = i.ToString();
    }
}

答案 2 :(得分:1)

  

如何从UI控件(例如文本框)进行线程安全调用   另一个使用TPL的线程?

你一般不做。

如果您需要更新UI,则使用Invoke执行此操作。但是直接来自TPL - 你不想经常更新用户界面,因为这总是很重要,除非你制作第一人称射击帧速率并不重要。每秒10次更新很多。

在这种情况下,您可能希望任务更新中央计数器(使用Interlocked类),并且一旦在更改中超过某个阈值(或使用并行运行的计时器)将其推送到UI中。