控制和取消多个任务

本文关键字:任务 取消 控制 | 更新日期: 2023-09-27 18:16:16

我试图编写简单的WinForm应用程序,我有一些问题与它的行为。目的是在面板上用两种可选的颜色(红色&黑色)。每种颜色有4个按钮-对- 绘制红色停止红色绘制黑色停止黑色

点击Draw按钮,生成DrawRectangles新任务。当单击stop按钮时,将使用CancellationTokenSource取消任务。

问题是:当我创建了许多任务(即绘制红色的3个任务),之后我按下停止按钮,它取消了只是第一个任务,我无法取消其他任务(绘制红色任务),他们跑了没完没了。

以下代码:

public partial class Form1 : Form
{
    CancellationTokenSource cnDrawRedToken;
    CancellationTokenSource cnDrawBlackToken;
    public Form1()
    {
        InitializeComponent();          
    }
    private void Form1_Load(object sender, EventArgs e{}
    private void panel1_Paint(object sender, PaintEventArgs e){}
    private void RedBtn_Click(object sender, EventArgs e)
    {
        RunDrawingTask(SetSpecificPen(Color.Red),out this.cnDrawRedToken);           
    }
    private async Task DrawRectangles(int height, int width, Random random, Rectangle rectangle, Pen blackPen, CancellationToken cnToken)
    {
        while(true)
        {
            if (cnToken.IsCancellationRequested)
                return;
            rectangle.x = random.Next(0, width);
            rectangle.y = random.Next(0, height);
            this.panel1.CreateGraphics().DrawRectangle(blackPen, rectangle.x, rectangle.y, rectangle.width, rectangle.height);
            //Thread.Sleep(200);
            await Task.Delay(150);
        }
    }
    private Pen SetSpecificPen(Color color)
    {
        Pen blackPen = new Pen(color, 2);
        return blackPen;
    }
    private Rectangle InitRectangleWidthAndHeights()
    {
        Rectangle rectangle = new Rectangle();
        rectangle.width = 10;
        rectangle.height = 10;
        return rectangle;
    }
    private void StpRedBtn_Click(object sender, EventArgs e)
    {
        if (this.cnDrawRedToken != null)
        {
            this.cnDrawRedToken.Cancel();
            this.cnDrawRedToken = null; 
        }
        else
        {
            this.cnDrawRedToken = new CancellationTokenSource();
        }
    }
    private void BlackBtn_Click(object sender, EventArgs e)
    {
        RunDrawingTask(SetSpecificPen(Color.Black),out this.cnDrawBlackToken);
    }
    private void StpBlkBtn_Click(object sender, EventArgs e)
    {
        if (this.cnDrawBlackToken != null)
        {
            this.cnDrawBlackToken.Cancel();
            this.cnDrawBlackToken = null; 
        }
    }
    private void RunDrawingTask(Pen specificPen, out CancellationTokenSource cnTokenSource) 
    {
        int height = this.panel1.Height;
        int width = this.panel1.Width;
        Random random = new Random();
        Rectangle rectangle = InitRectangleWidthAndHeights();
        cnTokenSource = new CancellationTokenSource();
        CancellationTokenSource cts = cnTokenSource;
        Task t = Task.Factory.StartNew(() => DrawRectangles(height, width, random, rectangle, specificPen, cts.Token), cts.Token);
    }
}

我想做的是当我点击停止按钮时,所有相同颜色的运行任务将被取消。

更新:正如法比奥所建议的,我已经重写了我的点击方法以及受影响的方法。在那之后,它就像预期的那样工作了。

public Form1()
    {
        InitializeComponent();
        cnDrawRedToken = new CancellationTokenSource() ;
        cnDrawBlackToken = new CancellationTokenSource();
    }
 private void RedBtn_Click(object sender, EventArgs e)
    {
        if (this.cnDrawRedToken.IsCancellationRequested)
        {
           this.cnDrawRedToken = null;
           this.cnDrawRedToken = new CancellationTokenSource();
        }           
        RunDrawingTask(SetSpecificPen(Color.Red), this.cnDrawRedToken);           
    }
private void StpRedBtn_Click(object sender, EventArgs e)
    {
        if (this.cnDrawRedToken != null)
        {
            this.cnDrawRedToken.Cancel();
        }          
    }
    private void BlackBtn_Click(object sender, EventArgs e)
    {
        if (this.cnDrawBlackToken.IsCancellationRequested)
        {
            this.cnDrawBlackToken = null;
            this.cnDrawBlackToken = new CancellationTokenSource();
        }             
        RunDrawingTask(SetSpecificPen(Color.Black), this.cnDrawBlackToken);
    }
    private void StpBlkBtn_Click(object sender, EventArgs e)
    {
        if (this.cnDrawBlackToken != null)
        {
            this.cnDrawBlackToken.Cancel();                
        }
    }
    private void RunDrawingTask(Pen specificPen, CancellationTokenSource cnTokenSource) 
    {
        int height = this.panel1.Height;
        int width = this.panel1.Width;
        Random random = new Random();
        Rectangle rectangle = InitRectangleWidthAndHeights();
        Task t = Task.Factory.StartNew(() => DrawRectangles(height, width, random, rectangle, specificPen, cnTokenSource.Token), cnTokenSource.Token);
    }

控制和取消多个任务

你的问题的根源是,如果你点击3次运行任务的按钮,你将运行3个任务与3个不同的取消令牌来源。

由于RunDrawingTask中的这一行,您将覆盖刚刚传递给RunDrawingTask的取消令牌

cnTokenSource = new CancellationTokenSource();

如果您删除了这一行,您将每次传递相同的令牌,并且您的所有任务将能够对取消做出反应。

您的逻辑对您的要求无效。解决方案之一:您需要创建两个CancelationTokenSources: redCancelationTokenSourceblackCancelationokenSource。然后像这样重写RunDrawingTask方法:

    private void RunDrawingTask(Pen pen, out CancellationTokenSource cnTokenSource)
    {
        int height = panel1.Height;
        int width = panel1.Width;
        Random random = new Random();
        Rectangle rectangle = InitRectangleWidthAndHeights();
        var cts = pen == blackPen ? blackTokenSource : redTokenSource;
        Task.Factory.StartNew(() => DrawRectangles(height, width, random, rectangle, pen, cts.Token), cts.Token);
    }
    CancellationTokenSource blackTokenSource = new CancellationTokenSource();
    CancellationTokenSource redTokenSource = new CancellationTokenSource();
    Pen blackPen = new Pen(Color.Black);
    Pen redPen = new Pen(Color.Red);

当你想停止绘制动作时,你必须在CancelationTokenSources: redCancelationSource或blackCancelationSource中调用'Cancel'方法