0

我在我的应用程序中实现了线程来抓取网站。在所有网站都被抓取之后,我想处理它们。

表单创建 queueworker(创建 2 个工作人员并处理任务)。完成所有任务后,我想在表单线程中处理它们。在这一点上,我接受了这个

public void WaitForCompletion()
    {
        // Enqueue one null task per worker to make each exit.
        StopWorkers();
        
        //wait for the workers to finish
        foreach (Thread worker in workers)
        {
            worker.Join();
        }
    }

执行任务后,我触发(来自 queueworker):

public event EventHandler<ProgressEvent> UrlScanned;   
 
if (UrlScanned != null)
{ 
         UrlScanned(this, new ProgressEvent(task.Name, 1));
}

并通过以下方式捕捉该事件:

urlscanner.UrlScanned += new EventHandler<ProgressEvent>(UrlScanningProgress);     

private void UrlScanningProgress(object sender, ProgressEvent args)
{
   if (pbarurlScan.InvokeRequired)
   {
       //don't use invoke when Thread.Join() is used! Deadlock
       Invoke(new MethodInvoker(delegate() { UrlScanningProgress(sender, args); 
       //BeginInvoke(new MethodInvoker(delegate() { UrlScanningProgress(sender, args)};
   }
   else
   {
                pbarurlScan.Value++;
   }
}

问题是窗体线程被阻塞并且调用 Invoke 整个应用程序现在处于死锁状态。

如何在没有死锁的情况下对窗体线程进行更新并进行即时更新(如果工作线程完成,则会发生开始调用)

4

2 回答 2

2

你为什么要加入?我会在每个线程结束时提出一个回调 - 可能会减少一个计数器。当计数器变为 0 时,回调UI 线程并执行“all done”代码;就像是:

using System.Windows.Forms;
using System.Threading;
using System;
class MyForm : Form
{

    public MyForm()
    {
        Button btn = new Button();
        Controls.Add(btn);
        btn.Text = "Go";
        btn.Click += btn_Click;
    }
    int counter;
    void btn_Click(object sender, System.EventArgs e)
    {
        for (int i = 0; i < 5; i++)
        {
            Interlocked.Increment(ref counter);
            ThreadPool.QueueUserWorkItem(DoWork, i);
        }
    }
    void DoWork(object state)
    {
        for (int i = 0; i < 10; i++)
        { // send progress
            BeginInvoke((Action)delegate { Text += state.ToString(); });
            Thread.Sleep(500);
        }
        EndThread(); // this thread done
    }
    void EndThread()
    {
        if (Interlocked.Decrement(ref counter) == 0)
        {
            AllDone();
        }
    }
    void AllDone()
    {
        Invoke((Action)delegate { this.Text += " all done!"; });
    }
    [STAThread]
    static void Main()
    {
        Application.Run(new MyForm());
    }
}
于 2009-02-06T12:33:29.197 回答
0

当所有任务完成后,我现在触发一个单独的事件。我将处理任务的逻辑移到了一个单独的函数中,该函数将在接收到 AllUrlsScanned 事件时被调用。

于 2009-02-06T13:00:01.780 回答