【问题标题】:Is there anything wrong with await Task.Run(() => semaphore.WaitOne())?await Task.Run(() => semaphore.WaitOne()) 有什么问题吗?
【发布时间】:2014-04-16 19:13:33
【问题描述】:

标题说明了一切。 await Task.Run(() => semaphore.WaitOne()); 有什么问题吗? System.Threading.Semaphore 不是线程仿射的,所以我认为不会有问题。我知道SemaphoreSlim类是可用的,但是我需要做跨进程同步,而SemaphoreSlim不做。

或者我可以/应该创建自己的 WaitHandle 自定义类型吗?

【问题讨论】:

  • 这样做在技术上没有问题。您是否应该这样做取决于您的帖子中未指定的更多细节。
  • 底层操作本质上是异步的,但是您有一个线程池线程同步等待异步操作,以便您可以异步指示何时完成。这通常是糟糕的设计,应尽可能避免。这表明您可能一开始就不应该使用Semaphore
  • 这表示您的错误或有问题的设计。我怀疑是前者——你说你“需要进行跨进程同步”,但这并不会真正同步任何东西。如果调用WaitOne的进程实际上需要知道信号量何时发出信号,为什么不同步等待呢?如果它不需要知道,那为什么还要有信号量呢?
  • 为了标题简洁,我省略了await,结果是Task。我会有类似的东西:await Task.Run(() => semaphore.WaitOne()) /* Do work */await Task.Run(() => { semaphore.WaitOne(); /* Do work */ })
  • 定义“错误”。简短的回答是:是的,对于“错误”的广泛定义。可能是您以错误的方式使用信号量 - 仅基于您发布的内容,它看起来更像命名事件会更好。 WaitOne() 无限期地等待,如果应用程序在信号量发出信号之前尝试退出会发生什么?如果信号量被遗弃,会发生什么?你没有处理异常。等等……

标签: c# .net multithreading thread-safety task-parallel-library


【解决方案1】:

如果您在此处等待信号量时尝试保持 UI 响应,这可能是有道理的,但有一个问题:"Semaphores don't have owners"。如果您在两个进程之间共享信号量,而另一个进程在没有调用 Semaphore.Release() 的情况下崩溃,则共享资源的所有权将丢失。剩余进程可能无法再次获取。

IMO,Mutex 语义在这里更合适,但使用 Mutex 你需要线程亲和性。也许,您可以获取互斥体,访问资源并在同一个线程上释放它:

await Task.Factory.StartNew(() => 
{
    mutex.WaitOne();
    try
    {
        // use the shared resource
    }
    finally
    {
        mutex.ReleaseMutex(); 
    }
}, TaskCreationOptions.LongRunnning);

如果这不可能(例如,因为您需要访问主 UI 线程上的共享资源),您可以为互斥锁使用专用线程。这可以通过自定义任务调度程序来完成,例如Stephen Toub 的 StaTaskSchedulernumberOfThreads:1 (在这种情况下,帮助线程不必成为 STA):

using (var scheduler = new StaTaskScheduler(numberOfThreads: 1))
{
    await Task.Factory.StartNew(
        () => mutex.WaitOne(), 
        CancellationToken.None,
        TaskCreationOptions.None,
        scheduler);
    try
    {
        // use the shared resource on the UI thread
    }
    finally
    {
        Task.Factory.StartNew(
            () => mutex.ReleaseMutex(), 
            CancellationToken.None,
            TaskCreationOptions.None,
            scheduler).Wait();
    }
}

已更新,如果您担心 WinRT(即 .NET for Windows Store Apps)或 Windows Phone,那么 Task.Factory.StartNew w/ TaskCreationOptions.LongRunning 仍然存在,您可以使用它来代替 @ 987654335@ 和 StaTaskScheduler 或类似我的 ThreadWithSerialSyncContext 的东西,只要您需要具有亲和力的后台线程。

【讨论】:

  • 不错的解决方案,但我认为它不适合我。我不能使用Mutex,因为我需要在获取信号量时等待一些任务,所以线程仿射锁是不行的。使用Wait()RunSynchronously() 也不是一种选择。您提出的StaTaskScheduler 解决方案听起来不错,但不幸的是,在WinRT API 的最新迭代中,Thread 类不存在。除非系统硬重置(无论如何都会清除信号量),如果我将同步代码包装在 try/finally 中,是否有可能放弃信号量?
  • @ZaneKaminski,如果你仔细平衡 WaitOne/Releasetry/finally 在任何地方,你应该可以接受这种方法。
  • @ZaneKaminski,还请查看我关于 WinRT 的更新。
  • 好主意。我什至没有想到这一点
【解决方案2】:

在等待原始信号量之前,您可以使用 SemaphoreSlim(0, 1)SemaphoreWaitAsync。这将确保对于每个进程,只有一个线程在等待原始Semaphore 时被阻塞,而其他线程在(a)等待WaitAsync 时不会被阻塞。

另一种解决方案是使用ConcurrentExclusiveSchedulerPair的独占调度器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-24
    • 2016-08-20
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-18
    相关资源
    最近更新 更多