C# Task.WaitAll阻塞主线程

6lilu9 2019-11-16 04:07:36
本人水平很菜,自学编程。遇到一个多线程无法更新UI界面的问题,
花了一天时间终于找到症结:Task.WaitAll阻塞主线程
又花了一天时间没找到答案。

以下代码会正确执行

private void button1_Click(object sender, EventArgs e)
{
ProFrm GetDataProFrm = new ProFrm(true)
{ Minimum = 0, Maximum =100, Title = "处理进度" };
for (int i = 0; i < 100; i++)
{
int K = i;
Task.Run(() =>
{
DoSomething(K);
GetDataProFrm.Progress_Add(K.ToString());
});
}
}


但有点要求没有达到,我实际工作中需要这100个DoSomething全部结束后才能进行下一步,
就是下面的代码

private void button2_Click(object sender, EventArgs e)
{
List<Task> Tsk = new List<Task>();

ProFrm GetDataProFrm = new ProFrm(true)
{ Minimum = 0, Maximum = 100, Title = "处理进度" };
for (int i = 0; i < 100; i++)
{
int K = i;
Tsk.Add(Task.Run(() =>
{
DoSomething(K);
GetDataProFrm.Progress_Add("123");
}));
}
Task.WaitAll(Tsk.ToArray());
}

但这代码明显是错的,因为进度条一直不动。

简单说明一下,主窗体上有两个按钮,另外还有一个窗体ProFrm ,里面放了一个进度条,目的就是进行过程中调动ProFrm上的进度条。



补充:
有网友给了我一种解决方法

private void button2_Click(object sender, EventArgs e)
{
Task.Run(() =>
{

List<Task> Tsk = new List<Task>();
ProFrm GetDataProFrm = new ProFrm(true)
{ Minimum = 0, Maximum = 100, Title = "处理进度" };
for (int i = 0; i < 100; i++)
{
int K = i;
Tsk.Add(Task.Run(() =>
{
DoSomething(K);
GetDataProFrm.Progress_Add("123");
}));
}
Task.WaitAll(Tsk.ToArray());
});

}

效果倒是可以,但没达到我的要求,我的需求是这100项工作全部结束,才能进行下一步,这样以来,100任务一闪而过,Task.WaitAll(Tsk.ToArray());就没意义了。
...全文
1747 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
wanghui0380 2019-11-18
  • 打赏
  • 举报
回复
上面使用plinq,但是会同步等待,如果是完全得异步,可以使用task.whenall
   private async void button1_Click_1(object sender, EventArgs e)
        {
            this.progressBar1.Maximum = 100;
            Progress<int> meProgress = new Progress<int>(p => this.progressBar1.Value = p);

            await doProgress(meProgress);
            MessageBox.Show("你说要进度条走完,然后再走一步,这里就是下一步,他走完才弹出来");

        }

        private async Task doProgress(IProgress<int> meProgress)
        {
            int count = 0;
          
          var tasks=  Enumerable.Range(1, 100).Select(p => { return doSomething(p).ContinueWith(c =>
                {
                    Interlocked.Increment(ref count);
                    meProgress.Report(count);
                }); });


          await Task.WhenAll(tasks);

        }

        private Random rand = new Random(DateTime.Now.Millisecond);
        public async Task doSomething(int i)
        {
            //模拟一个耗时间操作
            await Task.Delay(rand.Next(100, 1001));
            Trace.WriteLine(i);
        }
橘子皮... 2019-11-18
  • 打赏
  • 举报
回复
https://devblogs.microsoft.com/dotnet/async-in-4-5-enabling-progress-and-cancellation-in-async-apis/
wanghui0380 2019-11-18
  • 打赏
  • 举报
回复
net程序员的知识体系已经完全的混乱了,其实这里很多东西都是并不需要弄什么task.run,什么wait.all,这类东西的,他本来就是一个很顺畅的写法,别搞得别别扭扭
    private async void button1_Click_1(object sender, EventArgs e)
        {
            this.progressBar1.Maximum = 100;
            Progress<int> meProgress = new Progress<int>(p => this.progressBar1.Value = p);
            
            await doProgress(meProgress);
            MessageBox.Show("你说要进度条走完,然后再走一步,这里就是下一步,他走完才弹出来");

        }

        private async Task doProgress(IProgress<int> meProgress)
        {
            int count = 0;
            //直接plinq并行,没必要弄个Task.run
            foreach (var i in Enumerable.Range(1,100).AsParallel())
            {
                await doSomething(i);
                //并行操作没有顺序,你需要保证他的原子性
                Interlocked.Increment(ref count); 
                meProgress?.Report(count);
            }
        }

        private Random rand = new Random(DateTime.Now.Millisecond);
        public async Task doSomething(int i)
        {
            //模拟一个耗时间操作,故意给随机,是告诉你,你得任务并行执行,但是他其实是随机结束,并不按你循环顺序
            await Task.Delay(rand.Next(100, 1001));
        }
小风风12580 2019-11-18
  • 打赏
  • 举报
回复
ProgressBar progressBar1; public void DoProcessing(IProgress<int> progress) { ProFrm GetDataProFrm = new ProFrm(true) { Minimum = 0, Maximum =100, Title = "处理进度" }; int count = 0; for (int i = 0; i < 100; i++) { int K = i; Task.Run(() => { DoSomething(K); GetDataProFrm.Progress_Add(K.ToString()); count++; if (progress != null) { progress.Report(count); } }); } } private async void button1_Click(object sender, EventArgs e) { //当前线程 var progress = new Progress<int>(percent => {progressBar1.Value=percent}); //线程池线程 await Task.Run(() => DoProcessing(progress)); }
exception92 2019-11-18
  • 打赏
  • 举报
回复
这样以来,100任务一闪而过, -》一闪而过是因为for循环执行task任务,相当于还是异步执行了100个task任务。类似:

for (int i = 0; i < 100; i++)
                {
                    await Task.Run(()=>{
                       // TODO  处理逻辑
                    });
                    // TODO 执行其它操作。。。
                }
橘子皮... 2019-11-17
  • 打赏
  • 举报
回复
没办法 await task.run(function() ...) 嘛
wanghui0380 2019-11-17
  • 打赏
  • 举报
回复
Task.WhenAll(Tsk.ToArray()) 当然,这里其实有个问题的,并行处理其实没有顺序,你其实不能 int K = i;用这类东西去标记记录的,这个建议原子操作 另外,微软其实给你了一个通知类,可以直接通知的IProgress<T> 或者Progress<T>
hez2010 2019-11-16
  • 打赏
  • 举报
回复
        private async void button2_Click(object sender, EventArgs e)
        {
            List<Task> Tsk = new List<Task>();
 
            ProFrm GetDataProFrm = new ProFrm(true)
            { Minimum = 0, Maximum = 100, Title = "处理进度" };
            for (int i = 0; i < 100; i++)
            {
                int K = i;
                Tsk.Add(Task.Run(() =>
                {
                    DoSomething(K);
                    GetDataProFrm.Progress_Add("123");
                }));
            }
            await Task.WhenAll(Tsk.ToArray());
        }
chenxin7786 2019-11-16
  • 打赏
  • 举报
回复
我告诉你一个思路
在按钮事件刚进来的时候 btn.Enabled = false; 把你需要不能点的按钮都给禁用掉
你启动100个Task之后,你需要做的是来一个100任务完成之后的回调函数,而不是让主线程来等待。
那么具体代码如下

TaskFactory taskFactory = new TaskFactory();//创建TaskFactory工厂
taskFactory.ContinueWhenAll(Tsk.ToArray(), tArray => //这行代码不会卡住主线程,执行完100个任务后会回调
{
this.Invoke(new Action(btnConnEnd));// 利用主线程暴露的委托方法来更新UI,btnConnEnd是方法,你更新UI的方法
});

你可以在btnConnEnd方法里去让你需要可以点击的按钮Enabled = true;
我想我是确切明白了你的问题,并这个方案一定是能解决你得问题的,如果不明白,欢迎提问
Logerlink 2019-11-16
  • 打赏
  • 举报
回复
用await试试

        private async void button2_ClickAsync(object sender, EventArgs e)
        {
            List<Task> Tsk = new List<Task>();
            ProFrm GetDataProFrm = new ProFrm(true)
            { Minimum = 0, Maximum = 100, Title = "处理进度" };
            await Task.Run(() =>
            {
                for (int i = 0; i < 100; i++)
                {
                    int K = i;
                    DoSomething(K);
                    GetDataProFrm.Progress_Add("123");
                }
            });
            Console.WriteLine("此处会在最后输出");
        }

110,571

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧