【问题标题】:Enable/Disable buttons while thread is working线程工作时启用/禁用按钮
【发布时间】:2019-12-31 16:46:35
【问题描述】:

我有一个启动两个线程的按钮

private void CrawdBtn_Click(object sender, EventArgs e)
{
    CrawdBtn.Enabled = false;
    t = new Thread(AddLinksToList);
    b = new Thread(EnqueueFromList);
    t.Start();
    b.Start();            
}

还有其他按钮可以暂停、恢复、停止这些线程 我的问题是如何在线程工作时禁用(暂停、恢复、停止)按钮并在线程完成后重新启用 Crawl

【问题讨论】:

  • 使用async/await会比较容易做到,你反对这样做吗?
  • 仔细阅读this
  • Thread 类没有事件CompletedFinishedExited,因此您必须在AddLinksToListEnqueueFromList 两个方法中包含检查代码。如果你使用Tasks 而不是线程,它会更容易和更干净。
  • 那么 GSerg 的链接现在是你的朋友。

标签: c# multithreading winforms button


【解决方案1】:

您可以通过以下方式启动 Thread 并等待其完成:

public static Thread CreateAwaitableThread(Action action, out Task threadCompletion)
{
    var tcs = new TaskCompletionSource<bool>();
    threadCompletion = tcs.Task;
    return new Thread(() =>
    {
        try
        {
            action();
        }
        finally
        {
            tcs.SetResult(true);
        }
    });
}

此方法返回新创建的Thread,以及一个Task,将在Thread 完成时完成。你可以这样使用它:

private async void CrawdBtn_Click(object sender, EventArgs e)
{
    CrawdBtn.Enabled = false;
    Thread t1 = CreateAwaitableThread(AddLinksToList, out var t1c);
    Thread t2 = CreateAwaitableThread(EnqueueFromList, out var t2c);
    t1.Start();
    t2.Start();
    await Task.WhenAll(t1c, t2c);
    CrawdBtn.Enabled = true;
}

如果出现异常,错误将不会通过Task 传播。假定委托已经包含错误处理逻辑。否则,将照常发生未处理的异常。

【讨论】:

    【解决方案2】:

    为了解决您的问题,您可以创建一个线程来检查线程 t 和线程 b 的 ThreadState

    private void btnstart_Click(object sender, EventArgs e)
    {
      
        t = new Thread(AddLinksToList);
        b = new Thread(EnqueueFromList);
    
    
        t.Start();
        b.Start();
        if (threadchecker == null)//this if determines Whether it's the first time or not
        {
            threadchecker = new Thread(() => ChekingStateOfThreads());
            threadchecker.IsBackground = true;
            threadchecker.Start();
        }
    }
    

    由于线程想要检查那些线程的 ThreadState,它应该始终运行。 这是 ChekingStateOfThreads

    private void ChekingStateOfThreads()
    {
        while (true)
        {
            Thread.Sleep(1000);
       
            if (t.ThreadState == ThreadState.Stopped)
            {
                this.Invoke(new Action(() =>
                {
                    btnpause.Enabled = btnstart.Enabled = false;
                    btnresume.Enabled = btnstop.Enabled = true;
                }));
            }
            else if (t.ThreadState == ThreadState.Running)
            {
                this.Invoke(new Action(() =>
                {
                    btnstart.Enabled = btnresume.Enabled = false;
                    btnpause.Enabled = btnstop.Enabled = true;
                }));
            }
            else if (t.ThreadState == ThreadState.Aborted)
            {
                this.Invoke(new Action(() =>
                {
                    btnstart.Enabled = true;
                    btnpause.Enabled = btnresume.Enabled = btnstop.Enabled = false;
                }));
            }
            else if (t.ThreadState == ThreadState.Suspended)
            {
                this.Invoke(new Action(() =>
                {
                    btnpause.Enabled = btnstart.Enabled = false;
                    btnresume.Enabled = btnstop.Enabled = true;
                }));
            }
        }
    }
    

    函数的概念很简单。线程每 1 秒检查一次线程 t 的状态。

    请参阅Why use Invoke on Controls in .net? 了解我们为什么要使用 INVOKE。

    要中止线程检查器,只需使用 Form_closing 事件

    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
        threadchecker.Abort();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      相关资源
      最近更新 更多