【问题标题】:How to exclude timeout from lock wait? [duplicate]如何从锁定等待中排除超时? [复制]
【发布时间】:2015-02-17 03:24:45
【问题描述】:

我需要为我的函数调用设置超时以向 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
    }
});

但它不起作用。

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

【问题讨论】:

  • locking on this 是个好主意吗,是 Task 的标准(也许 this 在上下文中获取 Task,也许不是?)?
  • @GrantThomas 你可以假设obj 是一个实例字段,它没有任何区别,如果我有两个这样的函数,我就有麻烦了。你不能在一个线程中调用Monitor.Enter,而在另一个线程中调用Monitor.Exit
  • @decPL 这不是我的问题。我不知道这是否是指定超时和排除锁等待的正确方法,也许不是?
  • @alrz 您不能从超时中“排除”任意部分的执行时间(您如何想象这样的结构可以工作?)。您将“违规”部分移到代码之外的想法是正确的(尽管您可能希望单独解决阻塞“主”线程的问题)。
  • @alrz:你为什么要使用锁?您真正想解决什么问题?

标签: c# asynchronous timeout locking task


【解决方案1】:

您可以使用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");       
}

【讨论】:

  • 在您的情况下,您将如何实现 OP 的 if (TIMEOUT) 条件?我相当肯定Thread.Sleep 只是模拟了//do work 位。
  • 正如@decPL 所说,您的代码中没有指定超时以及您将如何处理它。除此之外,我使用的是.Net 4.0,所以没有异步/等待。 (我知道Microsoft.Bcl.Async 包)
  • 我更新了答案以显示如何使用CancellationTokenSource 的超时。此外,Task.Run 是 .NET 4.5,所以我假设您正在使用它。
  • @NedStoyanov 是的,没错。我将其更改为使用 Factory 方法。
  • @NedStoyanov 它是否解决了 alrz 的问题?我的脑内编译可能跟不上速度,但我相当肯定 await semaphore.WaitAsync() 不会被排除在 2 秒超时之外。
猜你喜欢
  • 2010-10-27
  • 2016-12-16
  • 2011-01-07
  • 1970-01-01
  • 1970-01-01
  • 2019-11-20
  • 2017-02-01
  • 2012-12-07
  • 2011-01-24
相关资源
最近更新 更多