如何异步运行3个进程,当一个返回所需的值时,停止另外两个并继续执行程序?

时间:2014-10-08 14:42:41

标签: c# asynchronous async-await

我有一个方法,使用aforge.net框架模板匹配(检查图像与另一个图像的相似性)对屏幕区域的许多单独的图像。此任务可能需要很长时间,或者可能接近即时,具体取决于图像数量,图像大小和要检查的区域。

图像列表中只有1个图像会返回匹配项,因此我想同时在屏幕上测试所有图像,此时其中1个图像返回true,其余进程立即取消,我的程序继续下一步。

现在,在我给出的示例中,我抓取一个基于哪个匹配返回true的整数值,但概念总是相同的。对屏幕截图测试的x个图像数量.1将返回true,其余的不会。有时候第一个返回true并且过程很好而且很快,有时它是列表中的第30个,同步匹配30个图像的模板与1相比需要相当长的时间。

关于我后面的代码需要注意的一点需要注意..我不会总是返回一个整数,我通常会返回一个布尔值来确定找到了哪个图像,但这里的代码是最简单的细节和相同的一般原则适用(即:如果我可以通过一种方式找到它,我可以做到另一种方式)。

目前我的(同步)代码读取如下...如何使这个异步调用能够完成我所描述的内容?如果可能的话,请详细说明您的答案,因为我打算学习,这样我以后就可以轻松做到这一点。我理解异步的概念但由于某种原因无法完全理解如何以我想要的方式去做。

    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;
    }

1 个答案:

答案 0 :(得分:0)

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

为简洁起见,我使用静态方法_templateMatch.Match(_tabula.BattleHeroTurnX())替换了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;
}

有关详细信息,请参阅thisthis