l versC#多线程问题

Posted

技术标签:

【中文标题】l versC#多线程问题【英文标题】:l versC# multithread issue 【发布时间】:2018-04-13 16:57:35 【问题描述】:

文件复制线程正在运行时,我无法让 ui 线程更新 ui。我的最终目标是让动画继续旋转,直到大文件复制最终完成,让用户知道程序没有冻结。这是一个非常简单的服务器到服务器文件复制程序。

谁能告诉我我做错了什么?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

using System.IO;
using System.Threading.Tasks;

namespace WindowsFormsApplication1

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

        private void ResetProgress()
        
            lblStep1.Image = null;
        

        private void SetupProgress()
        
            lblStep1.Image = global::animation1.Properties.Resources.animation;
        

        private void fileCopy()
        
            File.Copy("large file source", "large file destination", true);
        

        private void Form1_Load(object sender, EventArgs e)
        
            lblStep1.Image = global::animation1.Properties.Resources.animation;
        

        private async void button1_Click(object sender, EventArgs e)
        
            SetupProgress();
            await Task.Run(() => fileCopy());
            ResetProgress();
        

        private void btnStop_Click(object sender, EventArgs e)
        
            // unhandled currently
        
    

* 原始版本 *

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

using System.IO;
using System.Threading.Tasks;

namespace WindowsFormsApplication1

    public partial class Form1 : Form
    
        private Thread workItemsProducerThread;
        private Thread workItemsCopyThread;
        public Form1()
        
            InitializeComponent();
        

        private void ResetProgress()
        
            lblStep1.Image = null;
        

        private void SetupProgress()
        
        this.BeginInvoke((MethodInvoker)delegate ()
        
            lblStep1.Image = global::animation1.Properties.Resources.animation;
        );
    

        private void fileCopy()
                   
            File.Copy("Large file source", "Large file destination", true);

        this.BeginInvoke((MethodInvoker)delegate ()
        
            MessageBox.Show("Done");
        );

        

        private void Form1_Load(object sender, EventArgs e)
        
            lblStep1.Image = global::animation1.Properties.Resources.animation;
        

        private void btnStart_Click(object sender, EventArgs e)
        
        this.workItemsProducerThread = new Thread(new ThreadStart(this.SetupProgress));
        this.workItemsProducerThread.IsBackground = true;
        this.workItemsProducerThread.Start();

        this.SetupProgress();

            this.workItemsCopyThread = new Thread(new ThreadStart(this.fileCopy));
            this.workItemsCopyThread.IsBackground = true;
            this.workItemsCopyThread.Start();


        while (workItemsCopyThread.IsAlive)
        
            Thread.Sleep(1000); // wait
        

        MessageBox.Show("Done");
    

        private void btnStop_Click(object sender, EventArgs e)
        
            if (this.workItemsProducerThread != null)
            
                this.workItemsProducerThread.Abort();
                lblStep1.Image = global::animation1.Properties.Resources.animation;
            
        

        private void btnTest_Click(object sender, EventArgs e)
        
            fileCopy();
        
    

【问题讨论】:

相反,没有理由在另一个线程上运行 SetupProgress。这是个坏主意。 好的,我已经尝试了这两个建议,但症状仍然相同。我已经尝试从主 UI 运行 SetupProgress 并将 begininvoke 替换为仅调用,但行为没有改变。 @Jay 那是因为代码太复杂了。您不需要线程、调用或中止。 Henk Holterman 的回答显示了使用内置的 Task.Run 和 async/await 编写它是多么容易。 Panagiotis,你介意试试我的例子看看它是否适合你吗? fileCopy 应该是async 方法,并从Task 移出,你不需要这里的线程池——它是IO 方法,它们正是为async 设计的。 【参考方案1】:

不要在点击处理程序中睡觉。这会冻结 UI 线程。只需让时钟处理程序退出即可。在您的文件复制线程中,当副本不是时。使用 Invoke(或 BeginInvoke)使完成的消息框在 UI 线程上弹出。

【讨论】:

如果我理解正确,我已经进行了上述更正,但仍然遇到相同的症状? 当 Task.Run 和 async/await 可用时,没有理由使用 Invoke 或线程 您需要从点击处理程序中删除:while isalive 循环和消息框。 (顺便说一句,异步更容易)【参考方案2】:

试试这种老式风格

private void SetupProgress()
        

            Invoke((MethodInvoker) delegate
            
                lblStep1.Image = global::animation1.Properties.Resources.animation;
            );

        


 private Thread TDoSomeWork()
        
            var t = new Thread(() => DoSomeWork());
            t.Start();
            return t;
        


TDoSomeWork();

【讨论】:

以上是关于l versC#多线程问题的主要内容,如果未能解决你的问题,请参考以下文章

C#多线程之旅

c# 多线程的问题

C#多线程之旅

C#多线程之旅——介绍和基本概念

C#多线程之旅

C#多线程如何处理数据