【问题标题】:task deadlock when invoking in worker threads在工作线程中调用时任务死锁
【发布时间】:2014-06-07 22:28:07
【问题描述】:

我有一个 Windows 窗体程序 (**VS 2010 .NET 4 **),它递归地检测目录的文件夹和子文件夹并优化文件。 我通过任务库来做到这一点,并有一个显示项目进度的进度条和显示当前文件的进度条附近的标签。 我有一个用于此目的的 SetText(string text) 方法和委托 (delegate void SetTextCallback(string text);)。当我想在进程结束时显示一个消息框时,我认为它死锁了。但是当我在 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;
    }
}
}

我该如何处理?

【问题讨论】:

  • “我该如何处理?” - 删除task.Wait();。它在错误的时刻做错了事。
  • 我知道如果我删除它,它会起作用。所以我应该在工作完成后什么时候宣布用户?何时处理大量文件?
  • @Hamed_gibago starting a task and wait for it to finishrunning the code(editFiles) in the UI thread 之间没有区别。在这两种情况下,用户界面都会无响应。
  • 让 EditFiles 知道它的递归深度,并在顶层完成时引发一个事件。
  • 我该怎么做?

标签: c# task-parallel-library deadlock invoke


【解决方案1】:
    this.Invoke(d, new object[] { text });

使用任务的重点是不要等待它。如果你仍然这样做,那么你将挂起 UI 线程。它变得紧张,不再响应来自 Windows 的通知。包括您自己的代码生成的那些,例如 Invoke() 调用。它向 UI 线程发送一条消息,以查找要调用的委托。

所以 Invoke() 调用无法完成,UI 线程被挂起。 Task.Wait() 调用无法完成,因为该任务挂在 Invoke() 调用上。致命的拥抱,标准线程错误之一,称为死锁

注意 BeginInvoke() 没有这个问题,它不等待完成。解决了僵局,不为您提供实时更新,因为您仍然有一个对世界已死的 UI 线程。您必须删除 Wait() 调用。您可以通过使用 TaskScheduler.FromCurrentSynchronizationContext() 添加任务延续来运行 MessageBox.Show() 调用。而且你需要担心当用户关闭窗口时让这个任务停止,让它继续运行(通常)不会很好。 C# 版本 5 中添加的 async/await 关键字是解决此问题的更通用方法。

【讨论】:

  • 我添加一个条件 if (FileCounter == MaxFileCounter) MessageBox.Show("Finished");在 File.WriteAllBytes(fullpath, bt);在 writeFiles 方法中。检查作业是否完成并跟踪所有文件。并删除了 button6_Click 中的 task.wait() 。有用。但不是线程方式解决。这是一个很好的解决方案吗?我接受了你的回答。没关系。我会测试的。但谈谈我的解决方案。好还是坏?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多