在工作线程中调用时,任务死锁

本文关键字:任务 死锁 调用 工作 线程 | 更新日期: 2023-09-27 18:07:07

我有一个windows窗体程序(**VS 2010 . net 4 **),检测递归文件夹和子文件夹的目录和优化文件。我通过任务库和有一个进度条,显示项目的进度和标签附近的进度条显示当前文件来做到这一点。我有一个SetText(字符串文本)方法和委托(委托无效SetTextCallback(字符串文本);)用于此目的。当我想在进程结束时显示一个消息框时,我认为它死锁了。但是当我在button6_Click中不使用task.Wait();时,它会正常运行,UI不会挂起。这是我的代码:

    public partial class Form1 : Form
    {

int MaxFileCounter = 0;
    static int FileCounter = 0;
delegate void SetTextCallback(string text);
delegate void SetProgressCallback(int i);
private void button6_Click(object sender, EventArgs e)
{
    Task task = Task.Factory.StartNew(() =>
    {
        editFiles(txtFilePath.Text);
    });
    task.Wait();
    MessageBox.Show("finished");
}
private void editFiles(string directoryPath)
{
    try
    {
        //DirectoryInfo dirInfo = new DirectoryInfo(txtFilePath.Text);
        DirectoryInfo dirInfo = new DirectoryInfo(directoryPath);
        //string[] extensionArray = { ".jpg", ".png", ".gif" ,".jpeg"};
        string[] extensionArray = txtExtensionList.Text.Split(';');
        HashSet<string> allowedExtensions = new HashSet<string>(extensionArray, StringComparer.OrdinalIgnoreCase);
        FileInfo[] files = Array.FindAll(dirInfo.GetFiles(), f => allowedExtensions.Contains(f.Extension));
        writeFiles(files);
        foreach (DirectoryInfo dir in dirInfo.GetDirectories())
        {
            editFiles(dir.FullName);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
private void writeFiles(FileInfo[] files)
{
    try
    {
        foreach (FileInfo fileinfo in files)
        {
            MemoryStream mo;
            using (Image image = Image.FromFile(fileinfo.FullName))
            {
                SetText(fileinfo.FullName);
                FileCounter++;
                SetProgress(FileCounter * 100 / MaxFileCounter);
                mo = (MemoryStream)optimizeImage(image, int.Parse(txtPercent.Text));
            }
            byte[] bt = new byte[mo.Length];
            mo.Read(bt, 0, bt.Length);
            mo.Flush();
            mo.Close();
            string fullpath = fileinfo.FullName;
            File.WriteAllBytes(fullpath, bt);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
private void SetText(string text)
{
    // InvokeRequired required compares the thread ID of the
    // calling thread to the thread ID of the creating thread.
    // If these threads are different, it returns true.
    if (lblFileName.InvokeRequired)
    {
        SetTextCallback d = new SetTextCallback(SetText);
        this.Invoke(d, new object[] { text });
    }
    else
    {
        this.lblFileName.Text = text;
    }
}
private void SetProgress(int i)
{
    if (progressBar1.InvokeRequired)
    {
        SetProgressCallback p = new SetProgressCallback(SetProgress);
        this.Invoke(p, new object[] { i });
    }
    else
    {
        this.progressBar1.Value = i;
    }
}
}

我该如何处理?

在工作线程中调用时,任务死锁

    this.Invoke(d, new object[] { text });

使用Task的关键是不要等待它。如果你这样做,你将挂起UI线程。它变得紧张,不再响应来自Windows的通知。包括您自己的代码生成的,比如Invoke()调用。它发送一个消息给UI线程去寻找要调用的委托。

所以Invoke()调用不能完成,UI线程被挂起。task . wait()调用无法完成,因为任务挂起在Invoke()调用上。致命的拥抱,一个标准的线程错误叫做死锁

注意BeginInvoke()没有这个问题,它不等待完成。解决了死锁,但没有提供实时更新,因为你仍然有一个死气沉沉的UI线程。必须移除Wait()调用。你可以通过使用TaskScheduler.FromCurrentSynchronizationContext()添加一个任务延续来运行MessageBox.Show()调用。当用户关闭窗口时,您需要担心如何让这个任务停止,让它继续运行(通常)不会有好结果。c#版本5中添加的async/await关键字是这个问题的更通用的解决方案。