在.NET 4.0中将Task与Parallel.Foreach一起使用 [英] Using Task with Parallel.Foreach in .NET 4.0

查看:112
本文介绍了在.NET 4.0中将Task与Parallel.Foreach一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开始尝试向Windows窗体添加进度条,以更新Parallel.Foreach循环中运行的代码的进度.为此,UI线程必须可用以更新进度条.我使用一个Task运行Parallel.Foreach循环,以允许UI线程更新进度条.

I started off trying to add a progress bar to the windows form that updates the progress of code running within a Parallel.Foreach loop. In order to do this the UI thread has to be available to update the progress bar. I used a Task to run the Parallel.Foreach loop to allow the UI thread to update the progress bar.

在Parallel.Foreach循环中完成的工作非常繁琐.使用Task运行程序的可执行文件(不在Visual Studio中调试)后,程序变得无响应.如果我在没有Task的情况下运行程序,则不是这种情况.我注意到这两个实例之间的主要区别在于,在没有Task的情况下运行该程序会占用约80%的cpu,在有Task的情况下会占用5%的cpu.

The work done within the Parallel.Foreach loop is rather intensive. After running the executables of the program(not debugging within visual studio) with the Task, the program became unresponsive. This is not the case if I run my program without Task. The key difference I noticed between the two instances is that the program takes ~80% of the cpu when ran without Task, and ~5% when ran with Task.

private void btnGenerate_Click(object sender, EventArgs e)
    {
        var list = GenerateList();
        int value = 0;
        var progressLock = new object ();

        progressBar1.Maximum = list.Count();

        Task t = new Task(() => Parallel.ForEach (list, item =>
        {
                DoWork ();
                lock (progressLock)
                {
                    value += 1;
                }
        }));

        t.Start();

        while (!t.IsCompleted)
        {
            progressBar1.Value = value;
            Thread.Sleep (100);
        }
    }

侧面说明:我知道

 Interlocked.Increment(ref int___);

代替锁起作用.它被认为更有效吗?

works in place of the lock. Is it considered more efficient?

我的问题是三个方面:

1.)当负载少得多时,为什么带有Task的程序无响应?

1.) Why would the program with the Task become unresponsive when the load is much less?

2.)使用Task运行Parallel.Foreach是否将Parallel.Foreach的线程池限制为仅运行任务的线程?

2.) Does using Task to run Parallel.Foreach limit the thread pool of the Parallel.Foreach to only the thread running the task?

3.)是否有一种方法可以使UI线程响应,而不是在不使用取消令牌的情况下休眠0.1秒钟?

3.) Is there a way to make the UI thread responsive instead of sleeping for the .1 second duration without using cancellation token?

我非常感谢您的帮助或想法,我花了很多时间对此进行研究.如果违反任何发布格式或规则,我也深表歉意.我试图坚持使用它们,但可能错过了一些东西.

I'm grateful for any help or ideas, I've spent quite a lot of time researching this. I also apologize if I've violated any posting format or rules. I tried to adhere to them, but may have missed something.

推荐答案

您可以使用内置的Invoke方法在本地Windows同步上下文中调用委托来大大简化代码.

You can greatly simplify your code there by using the built in Invoke method that invokes a delegate on the owning Windows synchronization context.

来自 MSDN :

在拥有控件的基础窗口句柄的线程上执行指定的委托.

Executes the specified delegate on the thread that owns the control's underlying window handle.

如果当前控件的基础窗口句柄尚不存在,Invoke方法将在控件的父链中向上搜索,直到找到具有窗口句柄的控件或窗体为止.

The Invoke method searches up the control's parent chain until it finds a control or form that has a window handle if the current control's underlying window handle does not exist yet.

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

    string[] GenerateList() => new string[500];
    void DoWork()
    {
        Thread.Sleep(50);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var list = GenerateList();
        progressBar1.Maximum = list.Length;

        Task.Run(() => Parallel.ForEach(list, item =>
        {
            DoWork();

            // Update the progress bar on the Synchronization Context that owns this Form.
            this.Invoke(new Action(() => this.progressBar1.Value++));
        }));
    }
}

这将在Task内的Form所属的同一UI线程上调用Action委托.

This will invoke the Action delegate on the same UI thread that the Form belongs to, from within the Task.

现在尝试回答您的问题

1.)当负载少得多时,为什么带有Task的程序无响应?

1.) Why would the program with the Task become unresponsive when the load is much less?

我不确定100%,但这可能与您在UI线程上锁定成员有关.如果负载较小,则锁定会更频繁地发生,有可能导致UI线程在进度条增加时挂起".

I'm not 100% sure, but this could be related to you locking a member on the UI thread. If the load is less, then the lock will happen more frequently, potentially causing the UI thread to "hang" while the progressbar is incremented.

您还将运行一个while循环,该循环每100毫秒使UI线程休眠一次.您会看到由于while循环而导致UI挂起.

You are also running a while loop that is sleeping the UI thread every 100 milliseconds. You'll see UI hanging due to that while loop.

2.)使用Task运行Parallel.Foreach是否将Parallel.Foreach的线程池限制为仅运行任务的线程?

2.) Does using Task to run Parallel.Foreach limit the thread pool of the Parallel.Foreach to only the thread running the task?

不是.在Parallel.ForEach调用中将创建几个任务.基础ForEach使用分区程序进行传播进行锻炼,而不要创建超出必要范围的任务.它分批创建任务,并处理批处理.

It does not. Several tasks will get created within the Parallel.ForEach call. The underlying ForEach uses a partitioner to spread the work out, and not create more tasks than what is necessary. It creates tasks in batches, and processes the batches.

3.)是否有一种方法可以使UI线程响应,而不是在不使用取消令牌的情况下休眠0.1秒钟?

3.) Is there a way to make the UI thread responsive instead of sleeping for the .1 second duration without using cancellation token?

我能够通过删除while循环并使用Invoke方法直接在UI线程上执行lambda来解决这个问题.

I was able to handle that by removing the while loop and using the Invoke method to just go ahead and execute a lambda on the UI thread directly.

这篇关于在.NET 4.0中将Task与Parallel.Foreach一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆