【问题标题】:C# form, how to stop a thread with a button?C#表单,如何用按钮停止线程?
【发布时间】:2022-01-26 17:54:02
【问题描述】:

我在 C# 中做一个表单应用程序,它是一个从服务器接收带有套接字的字符串的客户端,我有一个在接收函数内部运行无限循环的线程。当我单击一个按钮时,我必须停止该线程,我尝试使用一个布尔变量,但它不起作用,因为该函数卡在接收函数上,并且对布尔变量的控制在 when 仍然为真之前完成。我该怎么做才能停止这个线程?

这是线程运行的函数:

 public void Prova()
        {                    
                while (true)
                {
                    string str = frmRegister.c.Receive();
                    ... doing things...
                }
        }

线程是这样启动的:

public Form1()
        {
            InitializeComponent();
            t1 = new Thread(Prova);
            t1.Start();       
        }

我必须在这里停止线程:

    private void goBack_Click(object sender, EventArgs e)
        {         
            new Form2().Show();           
            this.Hide();
        }

以下是我对 CancellationToken 的尝试:

private CancellationTokenSource tokenSource;

public void Prova(CancellationToken token)
       {         
           while(!token.IsCancellationRequested){           
               while (true)
               {
                   string str = frmRegister.c.Receive();
                   ... doing things...
               }
            }
       }

public Form1()
       {
         InitializeComponent();
         tokenSource = new CancellationTokenSource();
         var task = Task.Run(() => Prova(tokenSource.Token));       
       }

private void goBack_Click(object sender, EventArgs e)
       {      
           tokenSource.Cancel();
           new Form2().Show();           
           this.Hide();
       }

【问题讨论】:

  • 这有点超出我的回答范围,其他人可以帮你解决,但我相信如果你查找“CancellationToken”,你可能会找到一个很好的例子,应该插入您的场景。这就是我开始寻找的地方。
  • 我用 CancellationToken 尝试了一些东西,但它不起作用,也许我用错了方法。你能建议我如何使用它吗?很多人建议不要使用 Abort()
  • 我已经尝试过使用 CancellationToken 但它不起作用 -> 发布该代码。
  • @Kevin - 永远不要用.Abort() 停止线程。它可能会破坏运行时并使其余线程处于未知状态。唯一的例外是当您试图从应用程序中崩溃时。
  • @sommmen 是的,我编辑了问题

标签: c# multithreading forms loops while-loop


【解决方案1】:

根据您的示例,这应该可以:

private CancellationTokenSource tokenSource;

public void Prova(CancellationToken token = default)
       {         
           while(!token.IsCancellationRequested) {
                   string str = frmRegister.c.Receive();
                   ... doing things...
               }
            }
       }

甚至:

public void Prova(CancellationToken token = default)
        {                    
                while (true)
                {
                    token.ThrowIfCancellationRequested();

                    string str = frmRegister.c.Receive();
                    ... doing things...
                }
        }

或者:

public void Prova(CancellationToken token = default)
        {                    
                while (true)
                {
                    if(token.IsCancellationRequested)
                       return;

                    string str = frmRegister.c.Receive();
                    ... doing things...
                }
        }

基本上摆脱了双while循环,它可以工作。

【讨论】:

  • 您的 if(token.IsCancellationRequested)token.ThrowIfCancellationRequested(); 好得多 - throwing 会将编码问题发送给其他人。检查是否请求取消意味着您的代码负责。
  • @Enigmativity 值得商榷,视情况而定。在这种情况下,我觉得你是对的 - 无论如何这就是为什么该解决方案是最重要的;)
【解决方案2】:

你为什么用线程而不是任务?

你的情况

Thread th;
CancellationTokenSource cts = new CancellationTokenSource();

public void Prova(CancellationToken cancellationToken)
{                    
        while (true)
        {
            cancellationToken.ThrowIfCancellationRequested();
            string str = frmRegister.c.Receive();
            ... doing things...
        }
}

public Form1()
{
        InitializeComponent();
        th = new Thread(() => { Prova(cts.Token);  });
        th.Start();;       
}

private void goBack_Click(object sender, EventArgs e)
{         
    cts.Cancel();
}

有任务

    Task th;
    CancellationTokenSource cts = new CancellationTokenSource();

    public void Prova(CancellationToken cancellationToken)
    {
        while (true)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ...process
        }
    }

    public Form1()
    {
        InitializeComponent();
        th = Task.Run(() => { Prova(cts.Token); }, cts.Token);
        th.Start(); ;
    }

    private void goBack_Click(object sender, EventArgs e)
    {
        cts.Cancel();
    }

【讨论】:

  • 永远不要用.Abort() 停止线程。它可能会破坏运行时并使其余线程处于未知状态。唯一的例外是当您试图从应用程序中崩溃时。
  • 永远不要这样做new Form2().Show(); 。您应该始终保留对每个表单的引用(因为它们是IDisposable),以便您可以确保它被正确关闭。您需要释放表单的所有句柄。
  • 我尝试使用 CancellationToken 编辑了我的问题
  • Task.Run(() => { Prova(cts.Token); }) 更安全时,new Thread(() => { Prova(cts.Token); }) 毫无意义。
  • 在非 UI 线程中调用 frmRegister.c.Receive() 也是危险的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-19
  • 1970-01-01
  • 1970-01-01
  • 2021-11-15
  • 1970-01-01
  • 2017-07-13
相关资源
最近更新 更多