背景多线程(无响应的UI)

时间:2015-03-22 09:06:19

标签: c# multithreading user-interface backgroundworker freeze

基本上我一直在使用多线程,但它一直没有按照应有的方式工作。我立即尝试在标题中描述问题,以便在线找到答案,但没有发现任何关系。

这是我开发的一个应用程序,用于更好地理解多任务处理,以便在实际应用程序中有效使用。

enter image description here

基本上它的作用是你在文本框中写一些内容并选择你想要的线程数,它会在底部附加日志框。因此,它将显示每个线程运行了多少次 BUT 问题是它仍然会在后台冻结UI。

这里是它在3个线程上给出的输出(它可以处理少量(少于10个)线程):

enter image description here

这是完整的代码 -

public partial class thread : Form
{
    Thread[] threads;
    int amountOf;
    bool stop = false;
    int threadAmt = 0;

    public thread()
    {
        InitializeComponent();
    }

    delegate void SetTextCallback(string text);

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        amountOf = (int)amtThreads.Value;
        threads = new Thread[amountOf];
        //Define threads
        for (int i = 0; i < amountOf; i++)
        {
            threads[i] = new Thread(new ThreadStart(job));
        }
       //Start threads
       foreach (Thread t in threads)
       {
           t.IsBackground = true;
           t.Start();
       }
    }

    private void logIt(string text)
    {
        if (outputResult.InvokeRequired)
        {
            SetTextCallback cb = new SetTextCallback(logIt);
            Invoke(cb, new object[] { text });
        }
        else
            outputResult.AppendText(text);
    }

    private void job()
    {
        int threadNum = threadAmt++;
        int howMany = 0;
        do
        {
            howMany++;
            logIt("Thread: " + threadNum + " : Processed " + howMany + " times : Saying " + writeText.Text + Environment.NewLine);
            Thread.Sleep(1);
        }
        while (!stop);
    }

    private void Start_Click(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
        Start.Enabled = false;
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
            MessageBox.Show("Error: " + e.Error.Message);
        else if (e.Cancelled)
            MessageBox.Show("Application canceled.");
    }

    private void Stop_Click(object sender, EventArgs e)
    {
        stop = true;
        backgroundWorker1.CancelAsync();
        Stop.Enabled = false;
    }

    private void Clear_Click(object sender, EventArgs e)
    {
        outputResult.Clear();
    }
}

在放入10个或更多线程后,UI继续冻结。 我想知道的另一件事是暂停和恢复线程。当您停止线程循环然后重新启用它时,它只会显示一个结果。它不会继续循环。

1 个答案:

答案 0 :(得分:2)

你的问题不是创建过多的线程而是你的线程正在过度更新UI

你的job方法有一个非常紧凑的循环,你调用logIt然后睡眠一毫秒然后重复这个过程。日志编组调用UI线程调用outputResult.AppendText(text);。这种编组工作发生在消息泵上。

您实际上已经溢出了消息泵,基本上更新了TextBox,因此UI无法响应而无法处理任何其他内容。

将10个线程的数据相乘,所有日志记录之间的记录都很小,并且您的用户界面将会冻结。

考虑将Thread.Sleep增加至Thread.Sleep(500) 1/2秒。

更改此代码:

private void job()
{
    int threadNum = threadAmt++;
    int howMany = 0;
    do
    {
        howMany++;
        logIt("Thread: " + threadNum + " : Processed " + howMany + " times : Saying " + writeText.Text + Environment.NewLine);
        Thread.Sleep(1); // Oww!!  Should this have been 1000 instead of 1.   1000 is 1 second.
    }
    while (!stop);
}

...为:

private void job()
{
    int threadNum = threadAmt++;
    int howMany = 0;
    do
    {
        howMany++;
        logIt("Thread: " + threadNum + " : Processed " + howMany + " times : Saying " + writeText.Text + Environment.NewLine);
        Thread.Sleep(500); // e.g. 1/2 second 
    }
    while (!stop);
}

替代设计

你对线程很感兴趣。自从引入Thread以来,.NET使线程化变得更加容易,特别是当线程需要操作UI时。现在通常最好不要显式创建线程,而是使用备用API。

您可能希望通过asyncawait关键字查看.NET 异步方法,因为它们在您的情况下可能非常有用。

另一个时间的主题