正确使用Task.Run和Wait的方法

时间:2016-08-01 16:03:39

标签: .net task-parallel-library

我有一个由计时器启动的以下代码块:

private void CheckForScheduleItemsTimerOnElapsed(Object sender, ElapsedEventArgs elapsedEventArgs)
{
    if (CurrentlyProcessingMsgs)
        return;

    CurrentlyProcessingMsgs = true;

    var task = Task.Run(() => {
            CheckForScheduleItems.Process(_cancellationToken);
        }, _cancellationToken);

    task.Wait(_cancellationToken);

    CurrentlyProcessingMsgs = false;
}

现在我遇到的问题是,在某些情况下,行

CurrentlyProcessingMsgs = false;

没有被调用。据我所知,Task.Wait应该等待任务完成,然后继续,从而重置该标志。但在某些情况下,这种情况并没有发生。 (注意:此计时器由服务管理。)正在发生的事情是,在关机时,我正在检查 CurrentlyProcessingMsgs 的值,有时在任务完成后它仍然是真的。

这样做的目标是让多个定时器像上面显示的那样被触发。每个计时器将执行类似于上面显示的处理,我想在一个单独的线程中运行每个进程。我对CancellationToken的理解是错误的,所以在这个例子中,设置定时器和任务来运行这些方法并将每个方法放在一个单独的线程上的正确方法是什么?

1 个答案:

答案 0 :(得分:1)

如果_cancellationToken被取消,则会引发异常。如果你想确保标志被更改,即使它已被取消或Process引发了一些其他异常,请将CurrentlyProcessingMsgs = false;置于try / finally块中

private void CheckForScheduleItemsTimerOnElapsed(Object sender, ElapsedEventArgs elapsedEventArgs)
{
    if (CurrentlyProcessingMsgs)
        return;

    CurrentlyProcessingMsgs = true;
    try
    {
        var task = Task.Run(() => {
                CheckForScheduleItems.Process(_cancellationToken);
            }, _cancellationToken);

        task.Wait(_cancellationToken);
    }
    finally
    {
        CurrentlyProcessingMsgs = false;
    }
}

P.S。几乎没有理由做Task.Run然后立即等待它。如果执行以下操作会产生非常类似的逻辑,那么开销会少得多。

private void CheckForScheduleItemsTimerOnElapsed(Object sender, ElapsedEventArgs elapsedEventArgs)
{
    if (CurrentlyProcessingMsgs)
        return;

    CurrentlyProcessingMsgs = true;
    try
    {
        _cancellationToken.ThrowIfCancellationRequested();
        CheckForScheduleItems.Process(_cancellationToken);
    }
    finally
    {
        CurrentlyProcessingMsgs = false;
    }
}

这是一个更新版本,可以捕获异常。

private void CheckForScheduleItemsTimerOnElapsed(Object sender, ElapsedEventArgs elapsedEventArgs)
{
    if (CurrentlyProcessingMsgs)
        return;

    CurrentlyProcessingMsgs = true;
    try
    {
        _cancellationToken.ThrowIfCancellationRequested();
        CheckForScheduleItems.Process(_cancellationToken);
    }
    catch (OperationCanceledException ex)
    {
        //If the task was canceled for some other reason than our token raise the exception.
        if(ex.CancellationToken != _cancellationToken)
            throw;
    }
    finally
    {
        CurrentlyProcessingMsgs = false;
    }
}