如何异步运行3个进程,当其中一个返回所需值时,停止其他两个进程并继续执行程序

本文关键字:进程 其他 执行程序 继续 两个 返回 何异步 3个 运行 一个 异步 | 更新日期: 2023-09-27 18:26:00

我有一个方法,它使用aferge.net框架对屏幕的一个区域模板化(检查一个图像与另一个图像的相似性)多个单独的图像。这项任务可能需要很长时间,也可能几乎是即时的,具体取决于图像的数量、图像的大小和检查的区域。

图像列表中只有一个图像会返回匹配,所以我想同时在屏幕上测试所有图像,当这些图像中的1个返回真值时,剩余的过程将立即取消,我的程序将进入下一步。

现在,在我给出的例子中,我正在获取一个整数值,根据该整数值匹配将返回true,但概念始终相同。。x根据屏幕截图测试的图像数量。。1将返回true,其余将不返回。有时第一个返回true,过程又好又快,其他时候是列表中的第30个,与1相比,同步匹配30个图像的模板需要相当长的时间。

关于下面的代码,需要注意一点。。我不会总是返回一个整数,我通常会返回一个布尔值,说明找到了哪个图像,但这里的代码最容易详细说明,同样的一般原理也适用(即:如果我能用一种方法计算出来,我就能用另一种方法)。

目前我的(同步)代码如下。。。我该如何使这个异步调用能够实现我所描述的功能?如果可能的话,请详细说明你的答案,因为我打算学习,这样我将来就可以很容易地做这类事情。我理解异步的概念,但由于某种原因,我无法完全理解如何按照我想要的方式进行。

    public void Battle()
    {
        var myGuysTurn = WhosTurn();
        // other logic here.
    }
    private int WhosTurn()
    {
        var whosTurn = 0;
        var whosTurnCheck = _templateMatch.Match(_tabula.BattleHeroTurn1());
        if (whosTurnCheck)
        {
            whosTurn = 1;
            return whosTurn;
        }
        whosTurnCheck = _templateMatch.Match(_tabula.BattleHeroTurn2());
        if (whosTurnCheck)
        {
            whosTurn = 2;
            return whosTurn;
        }
        whosTurnCheck = _templateMatch.Match(_tabula.BattleHeroTurn3());
        if (whosTurnCheck)
        {
            whosTurn = 3;
            return whosTurn;
        }
        return whosTurn;
    }

如何异步运行3个进程,当其中一个返回所需值时,停止其他两个进程并继续执行程序

我会将Task.WaitAny()CancellationToken结合使用。从本质上讲,并行启动每个任务,并等待任何任务完成。如果已完成的任务成功,请取消其他任务。如果没有,请继续等待其他任务完成。

为了简洁起见,我用静态方法BattleHeroTurnX替换了_templateMatch.Match(_tabula.BattleHeroTurnX())

private int WhosTurn()
{
    // Create cancellation token. Will be used to inform other threads that they should immediately cancel processing
    CancellationTokenSource cts = new CancellationTokenSource();
    // Collection of tasks that run in parallel
    List<Task<int>> tasks = new List<Task<int>>()
    {
        Task.Run<int>(() => {
            return BattleHeroTurn1(cts.Token) ? 1 : 0; 
        }),
        Task.Run<int>(() => {
            return BattleHeroTurn2(cts.Token) ? 2 : 0;
        }),
        Task.Run<int>(() => {
            return BattleHeroTurn3(cts.Token) ? 3 : 0;
        })
    };
    // Wait for any task to complete and if it is successful, cancel the other tasks and return
    while (tasks.Any())
    {
        // Get the index of the task that completed
        int completedTaskIndex = Task.WaitAny(tasks.ToArray());
        int turn = tasks[completedTaskIndex].Result;
        if(turn > 0)
        {
            cts.Cancel();
            return turn;
        }
        tasks.RemoveAt(completedTaskIndex);
    }
    // All tasks have completed but no BattleHeroTurnX returned true
    return 0;
}
static bool BattleHeroTurn1(CancellationToken token)
{
    // Processing images. After each one is processed, ensure that the token has not been canceled
    for(int i = 0; i < 100; i++)
    {
        Thread.Sleep(50);
        if (token.IsCancellationRequested)
        {
            return false;
        }
    }
    return true;
}
static bool BattleHeroTurn2(CancellationToken token)
{
    // Processing images. After each one is processed, ensure that the token has not been canceled
    for (int i = 0; i < 10; i++)
    {
        Thread.Sleep(70);
        if (token.IsCancellationRequested)
        {
            return false;
        }
    }
    return true;
}
static bool BattleHeroTurn3(CancellationToken token)
{
    // Processing images. After each one is processed, ensure that the token has not been canceled
    for (int i = 0; i < 1000; i++)
    {
        Thread.Sleep(500);
        if (token.IsCancellationRequested)
        {
            return false;
        }
    }
    return true;
}

有关更多信息,请参阅此和此。