await Task.Run(() => semaphore.WaitOne()) 有什么问题吗?该内容涉及IT技术。

9
标题已经说明一切。 await Task.Run(() => semaphore.WaitOne());有什么问题吗?System.Threading.Semaphore不是线程相关的,因此我认为不会有问题。我知道SemaphoreSlim类可用,但我需要跨进程同步,而SemaphoreSlim无法实现。或者我可以/应该创建自己的WaitHandle自定义类型吗?

6
技术上做这件事情没有问题。但是你是否应该这样做取决于你帖子中未指定的许多细节。 - Michael Gunter
3
底层操作本质上是异步的,但你却让线程池线程同步等待异步操作,以便在完成时异步指示。这通常是设计不良,并且应该尽可能避免。这表明你可能本来就不应该使用信号量(Semaphore) - Servy
这表明可能是你的错误或者设计存在问题。我怀疑是前者——你说你“需要进行跨进程同步”,但这并不会真正实现同步。如果调用WaitOne的进程实际上需要知道信号量何时被触发,为什么不同步等待呢?如果它不需要知道,那为什么还要使用信号量呢? - nobody
为了在标题中简洁明了,我省略了对产生的“Task”进行等待。我会这样做:await Task.Run(() => semaphore.WaitOne()) /* Do work */ 或者 await Task.Run(() => { semaphore.WaitOne(); /* Do work */ }) - Zane Kaminski
2
定义“错误”。简短的回答是:对于“错误”的广泛定义而言,是的。可能是因为您正在错误地使用信号量——仅根据您发布的内容,似乎命名事件会更好。WaitOne()无限期等待,如果应用程序在信号量被标记之前尝试退出会发生什么?如果信号量被遗弃会发生什么?您没有处理异常。等等... - Peter Ritchie
这是什么类型的应用程序?手机?ASP.NET? - usr
2个回答

8
如果您想在等待信号量时保持UI响应,这可能是有意义的,但有一个问题:"信号量没有所有者"。如果您在两个进程之间共享信号量,并且另一个进程崩溃而没有调用Semaphore.Release()则共享资源的所有权将丢失。剩余的进程可能无法再次获取它。
在我看来,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,则仍然可以使用带有TaskCreationOptions.LongRunningTask.Factory.StartNew,而不是使用new Thread()StaTaskScheduler之类的东西,或者像我这样使用ThreadWithSerialSyncContext,每当您需要具有亲和性的后台线程时。

不错的解决方案,但我认为它对我不起作用。我不能使用Mutex,因为我需要在获取信号量时等待一些任务,所以线程关联锁是不可行的。使用Wait()RunSynchronously()也不是一个选项。你提出的StaTaskScheduler解决方案听起来不错,但不幸的是,在最新的WinRT API迭代中,Thread类不存在了。除非进行系统硬重置(这将清除信号量),否则如果我在try/finally中包装同步代码,会有任何遗弃信号量的可能性吗? - Zane Kaminski
@ZaneKaminski,如果你在每个地方都仔细平衡WaitOne/Releasetry/finally,那么你使用这种方法应该是没问题的。 - noseratio - open to work
@ZaneKaminski,还请查看我关于WinRT的更新。 - noseratio - open to work
很好的想法。我甚至没有考虑过那个。 - Zane Kaminski

0

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接