如何从锁定等待中排除超时?

时间:2014-12-18 10:56:47

标签: c# asynchronous timeout locking task

我需要为函数调用设置超时,以便向ui发送反馈。

Task.Factory.StartNew(() => {

    var task = Task.Factory.StartNew(() => {

        lock(this) {

            // do work
            Thread.Sleep(5000);                
        }
    });

    // if the lock above blocks the thread, this might be timed out

    if(task.Wait(timeout)) {
        // done
    } else {
        // timeout
    }
});

我的初步解决方案是我将锁定置于内部任务之外

Task.Factory.StartNew(() => {

    Monitor.Enter(obj);
    var task = Task.Factory.StartNew(() => {


        // do work

        Thread.Sleep(5000);
        Monitor.Exit(obj);                

    });

    // if the lock above blocks the thread, task won't start.

    if(task.Wait(timeout)) {
        // done
    } else {
        // timeout
    }
});

但它没有用。

我的问题是,如何为我的任务指定超时但排除锁定等待?

1 个答案:

答案 0 :(得分:0)

您可以使用SemaphoreSlim进行异步等待。此外,一旦你这样做,你将不需要外部Task.Run,但我把它留在了。你可以使用Task.Delay而不是Thread.Sleep,因为前者不阻止当前线程,异步等待,也可以通过CancellationToken取消。

private SemaphoreSlim semaphore = new SemaphoreSlim(1,1);

// Add ability to cancel work
public async Task DoWorkAsync(Action work, CancellationToken token)
{
    await Task.Run(async () =>
    {         
        try
        {
            await semaphore.WaitAsync(token);
            await Task.Run(work, token);
        }
        finally     
        {
            semaphore.Release();
        }

        // use await Task.Delay rather than Thread.Sleep as it doesn't use a thread
        // and stops almost immediately when cancellation requested.
        await Task.Delay(5000, token);
    }, token);
}

要创建超时情节,请创建一个超时的CancellationTokenSource并抓住OperationCanceledException以观察超时。

try
{
    var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));

    await DoWorkAsync(() => /*do work*/, cts.Token);
}
catch(OperationCanceledException ex)
{
    Console.WriteLine("Operation timed out");       
}