退出异步无限循环

时间:2017-09-30 10:53:05

标签: c# winforms loops asynchronous exit

我在WinForm应用程序中启动了一些异步无限循环,但每次我试图突破它们时,程序都会挂起。我已经阅读了一些人们建议使用CancellationTokens的类似主题,但我无法根据自己的需要调整它们。这是我的代码的相关部分。

static bool processStop = false;
static bool processStopped = false;

//Called once 
private async void ProcessData()
{
    while (!processStop)
    {
        await Task.Run
        (
            () =>
            {
                //Do stuff and call regular not async methods
            }
        );
    }
    processStopped = true;
}

//Button click handler to exit WinForm
btnExit.Click += (senders, args) =>
{
    processStop = true;
    //Programm hangs up here

    while (!processStopped);

    FormMain.Close();                
}

编辑代码
变量是静态的 Close方法是Forms的默认Close()方法。

2 个答案:

答案 0 :(得分:3)

问题是对Task.Run的调用在主线程上继续。 processStop = true;while (!processStopped);一个接一个地同步执行。这不会让ProcessData方法继续执行并发生死锁 我看到了几个解决方案:

  • ConfigureAwait(false)Task.Run

    一起使用
    private async void ProcessData()
    {
        while (!processStop)
        {
            await Task.Run
            (
                () =>
                {
                    //Do stuff and call regular not async methods
                }
            ).ConfigureAwait(false);
        }
        processStopped = true;
    }
    

    这将导致ProcessData继续在线程池上,并且您已经通过调用Task.Run使用了线程池,因此它不是一个很好的解决方案

  • 将整个过程包裹在Task.Run

    static volatile bool processStop = false;
    static volatile bool processStopped = false;
    
    //Called once 
    private async void ProcessData()
    {
        await Task.Run(() =>
        {
            while (!processStop)
            {
                ...
            }
            processStopped = true;
        });
    }
    

    这需要更改传递给它的方法的形式。

  • 使ProcessData成为一种同步方法来处理CPU密集型任务并正确调用它。 CancellationToken将是取消任务的首选方式:

    private void ProcessData(CancellationToken token)
    {
        while(!token.IsCancellationRequested)
        {                
            // do work
        }
    }
    

    用这个称呼它:

    Task processingTask;
    CancellationTokenSource cts;
    
    void StartProcessing()
    {
        cts = new CancellationTokenSource();
        processingTask = Task.Run(() => ProcessData(cts.Token), cts.Token);
    }
    
    btnExit.Click += async (senders, args) =>
    {
        cts.Cancel();
        try
        {
            await processingTask;
        }
        finally
        {
            FormMain.Close();
        }                
    }
    

答案 1 :(得分:0)

如果你想在不阻塞的情况下旋转一堆任务,你可以这样做:

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //Called once 
        private async Task ProcessData()
        {
            int count = 0;
            while (true)
            {
                await Task.Run
                 (
                     () =>
                     {
                         this.Invoke(new Action(() => {
                             label2.Text = (count++).ToString();
                             label1.Text = DateTime.Now.ToString(); }));
                         Thread.Sleep(100);
                     }
                 );
            }
            Debugger.Break(); //you will never see this hit at all
        }
        private void button1_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private async void button2_Click(object sender, EventArgs e)
        {
           await ProcessData();
        }
    }
}