Is there anything wrong with await Task.Run(() => semaphore.WaitOne())?

后端 未结 2 1926
遥遥无期
遥遥无期 2020-12-31 18:28

Title says it all. Is there anything wrong with await Task.Run(() => semaphore.WaitOne());? System.Threading.Semaphore isn\'t thread-affine, so

相关标签:
2条回答
  • 2020-12-31 18:46

    You can use a SemaphoreSlim(0, 1) WITH your Semaphore , and WaitAsync before waiting for your original semaphore. This would ensure that for each process, only one thread is blocked waiting for the original Semaphore, and the others, while (a)waiting for WaitAsync, is not blocked.

    Another solution is to use the exclusive scheduler of ConcurrentExclusiveSchedulerPair.

    0 讨论(0)
  • 2020-12-31 18:54

    If you're trying to keep the UI responsive while waiting for the semaphore here, it might make sense, but there's a catch: "Semaphores don't have owners". If you share the semaphore between two processes, and the other process crashes without calling Semaphore.Release(), the ownership over the shared resource will be lost. The remaining process may not be able to acquire it again.

    IMO, the Mutex semantic would be more appropriate here, but with Mutex you'd need thread affinity. Perhaps, you can acquire the mutex, access the resource and release it on the same thread:

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

    If that's not possible (e.g., because you need to access the shared resource on the main UI thread), you could use a dedicated thread for the mutex. This can be done with a custom task scheduler, e.g. Stephen Toub's StaTaskScheduler with numberOfThreads:1 (the helper thread doesn't have to be made STA in this case):

    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();
        }
    }
    

    Updated, if you're concerned about WinRT (i.e., .NET for Windows Store Apps) or Windows Phone, then Task.Factory.StartNew w/ TaskCreationOptions.LongRunning is still there, you can use it instead of new Thread() with StaTaskScheduler or something like my ThreadWithSerialSyncContext whenever you need a background thread with affinity.

    0 讨论(0)
提交回复
热议问题