如何在WinForms C#中使用线程?

3

我有一个与线程使用相关的问题。我的应用程序是WinForms,其中有一个带有方法的开始按钮。当我点击它时,该方法开始执行并持续很长时间。在方法执行期间,窗体不活动,我甚至不能关闭它直到方法结束。我想让窗体变为活动状态,并点击另一个按钮(停止按钮)以停止方法的执行。

    private void start_Click(object sender, EventArgs e)
    {
        StartLoading() //Some Method which performing I want to stop at any time
    }

    private void stop_Click(object sender, EventArgs e)
    {
        //Stop performing Method from start_Click
    }

我将尝试使用下面的代码:

我尝试使用下面的代码:

    private void start_Click(object sender, EventArgs e)
    {
        Thread StartThread = new Thread(StartLoading);
        StartThread.Start();
    }

    public void StartLoading() 
    {
    }

它可以工作。这种方法在表单保持活动状态时执行。但我不知道如何在stop_Click事件中停止此线程。也许有另一种方法可以实现我想要的功能?

最好的问候 Sergey


StartThread.Abort(); - Behzad
3个回答

4

如果您想执行一个缓慢操作直到其完成或手动取消它,您可能需要使用TaskCancellationToken。对我来说,这似乎是最合适的方法:

public class MyForm
{
    private CancellationTokenSource cts = new CancellationTokenSource();
    private Task task;

    private void buttonStart_Click(object sender, EventArgs e) 
    {
        buttonStart.Enabled = false;
        buttonCancel.Enabled = true;

        task = Task.Factory.StartNew(() => {
            // do something extremely slow
            // and use 'ThrowIfCancellationRequested'

            for (int i = 0; i < Int32.MaxValue; i++)
            {
                Thread.Sleep(10);
                cts.Token.ThrowIfCancellationRequested();
            }
        }, cts.Token).ContinueWith(t => {
            if (t.IsCanceled)
            {
                // User has cancelled loading
            }
            if (t.IsFaulted)
            {
                // Exception has occured during loading
            }
            if (t.IsCompleted)
            {
                // Loading complete
            }
        });
    }       

    private void buttonCancel_Click(object sender, EventArgs e)
    {
        buttonStart.Enabled = true;
        buttonCancel.Enabled = false;

        cts.Cancel();
    }
}

如果你想要在任务开始后取消并且让t.IsCanceled返回true,我认为你需要使用cts.Token.ThrowIfCancellationRequested()而不是if (cts.IsCancellationRequested) return;。如果你不这样做,任务会认为取消请求已经发出,但方法已经完成它的工作,所以任务会将其标记为IsCompleted - Scott Chamberlain
@ScottChamberlain 当然,你是对的。我已经编辑了我的回答。我还缺少了什么try .. catch代码吗?或者它会自己正确处理OperationCanceledException异常吗?谢谢。 - Yeldar Kurmangaliyev
如果OperationCanceledException与传递给StartNew作为第二个参数的令牌相关联,则它被视为一种特殊情况,并转到IsCanceled状态,而不是IsFaulted状态。如果引发了OperationCanceledException但未与传入的令牌关联,则它将显示为故障任务。 - Scott Chamberlain
请参阅MSDN页面“如何取消任务及其子任务”以获取详细示例。 - Scott Chamberlain

2
如果您在线程内部进行循环,我建议您添加一个变量,例如这样:
在停止按钮中添加以下内容:
bool isStopped = false;

在你的循环内部:

while(yourCondition)
{
    if(isStopped)
       break;
}

这样做更加安全,可以确保你完成当前循环。

但是如果你想立即终止它,有一个名为Thread.Abort()的函数。

你需要像这样修改代码:

Thread StartThread = null;
private void start_Click(object sender, EventArgs e)
{
    StartThread = new Thread(StartLoading);
    StartThread.Start();
}

public void StartLoading() 
{
    StartThread.Abort();
}

1
请注意,调用 StartThread.Abort() 将引发异常,应该捕获。 - Yeldar Kurmangaliyev
1
谢谢。你的答案很有效。它很简单易懂。其他的答案对我来说太难理解了)) - Sergey
2
@Sergey Thread.Abort()【是一个糟糕的选择】(https://dev59.com/_HI_5IYBdhLWcg3wBuT3),它可能会导致您的程序处于损坏状态。不要这样做。 - Scott Chamberlain
是的,所以我找到了一个解决办法,并告诉操作员如果他确实需要立即终止线程,则使用此方法。 - User2012384
1
我认为Thread.Abort()对我来说是好的。因为我很少使用它,只有在出现问题并且我想立即停止程序时,我才会使用它,我不会关心它的可行性。谢谢! - Sergey

1
您可以使用 BackgroundWorker 来实现此功能。
        private BackgroundWorker bw = new BackgroundWorker();

        public Form()
        {
            InitializeComponent();

            bw.WorkerSupportsCancellation = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        }

        private void start_Click(object sender, EventArgs e)
        {
            if (!bw.IsBusy)
            {
                bw.RunWorkerAsync();
            }
        }
        private void stop_Click(object sender, EventArgs e)
        {
            if (bw.WorkerSupportsCancellation)
            {
                bw.CancelAsync();
            }
        }
        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if (worker.CancellationPending)
            {
                e.Cancel = true;
                return;
            }

            StartLoading(); //Some Method which performing I want to stop at any time
        }
        private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                //"Canceled!";
            }

            else if (e.Error != null)
            {
                //"Error: " + e.Error.Message);
            }

            else
            {
                //"Done!";
            }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接