我已阅读SemaphoreSlim SemaphoreSlim MSDN的文档 这表示如果您将其配置为:SemaphoreSlim将限制一段代码一次只运行1个线程:
SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
但是,它并不表示是否阻止相同的线程访问该代码。这提出了异步和等待。如果在方法中使用await,则控制将离开该方法,并在任何任务或线程完成时返回。在我的示例中,我使用了一个带有异步按钮处理程序的按钮。它使用' await'调用另一种方法(Function1)。 Function1依次调用
await Task.Run(() => Function2(beginCounter));
围绕我的Task.Run()我有一个SemaphoreSlim。肯定看起来它会阻止同一个线程进入Function2。但是从文档中我无法保证这一点(我读过),我想知道是否可以依靠它。
我在下面发布了完整的例子。
谢谢,
戴夫
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace AsynchAwaitExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
public MainWindow()
{
InitializeComponent();
}
static int beginCounter = 0;
static int endCounter = 0;
/// <summary>
/// Suggest hitting button 3 times in rapid succession
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button_Click(object sender, RoutedEventArgs e)
{
beginCounter++;
endCounter++;
// Notice that if you click fast, you'll get all the beginCounters first, then the endCounters
Console.WriteLine("beginCounter: " + beginCounter + " threadId: " + Thread.CurrentThread.ManagedThreadId);
await Function1(beginCounter);
Console.WriteLine("endCounter: " + endCounter + " threadId: " + Thread.CurrentThread.ManagedThreadId);
}
private async Task Function1(int beginCounter)
{
try
{
Console.WriteLine("about to grab lock" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter);
await _semaphoreSlim.WaitAsync(); // get rid of _semaphoreSlim calls and you'll get into beginning of Function2 3 times before exiting
Console.WriteLine("grabbed lock" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter);
await Task.Run(() => Function2(beginCounter));
}
finally
{
Console.WriteLine("about to release lock" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter);
_semaphoreSlim.Release();
Console.WriteLine("released lock" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter);
}
}
private void Function2(int beginCounter)
{
Console.WriteLine("Function2 start" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter);
Thread.Sleep(1000);
Console.WriteLine("Function2 end" + " threadId: " + Thread.CurrentThread.ManagedThreadId + " beginCounter: " + beginCounter);
return;
}
}
}
如果单击按钮3次,则输出示例。请注意,Function2总是在给定计数器重新启动之前完成。
beginCounter: 1 threadId: 9
about to grab lock threadId: 9 beginCounter: 1
grabbed lock threadId: 9 beginCounter: 1
Function2 start threadId: 13 beginCounter: 1
beginCounter: 2 threadId: 9
about to grab lock threadId: 9 beginCounter: 2
beginCounter: 3 threadId: 9
about to grab lock threadId: 9 beginCounter: 3
Function2 end threadId: 13 beginCounter: 1
about to release lock threadId: 9 beginCounter: 1
released lock threadId: 9 beginCounter: 1
grabbed lock threadId: 9 beginCounter: 2
Function2 start threadId: 13 beginCounter: 2
endCounter: 3 threadId: 9
Function2 end threadId: 13 beginCounter: 2
about to release lock threadId: 9 beginCounter: 2
released lock threadId: 9 beginCounter: 2
endCounter: 3 threadId: 9
grabbed lock threadId: 9 beginCounter: 3
Function2 start threadId: 13 beginCounter: 3
Function2 end threadId: 13 beginCounter: 3
about to release lock threadId: 9 beginCounter: 3
released lock threadId: 9 beginCounter: 3
endCounter: 3 threadId: 9
如果你摆脱了SemaphoreSlim的电话,你会得到:
beginCounter: 1 threadId: 10
about to grab lock threadId: 10 beginCounter: 1
grabbed lock threadId: 10 beginCounter: 1
Function2 start threadId: 13 beginCounter: 1
beginCounter: 2 threadId: 10
about to grab lock threadId: 10 beginCounter: 2
grabbed lock threadId: 10 beginCounter: 2
Function2 start threadId: 14 beginCounter: 2
beginCounter: 3 threadId: 10
about to grab lock threadId: 10 beginCounter: 3
grabbed lock threadId: 10 beginCounter: 3
Function2 start threadId: 15 beginCounter: 3
Function2 end threadId: 13 beginCounter: 1
about to release lock threadId: 10 beginCounter: 1
released lock threadId: 10 beginCounter: 1
endCounter: 3 threadId: 10
Function2 end threadId: 14 beginCounter: 2
about to release lock threadId: 10 beginCounter: 2
released lock threadId: 10 beginCounter: 2
endCounter: 3 threadId: 10
答案 0 :(得分:16)
SemaphoreSlim类在调用Wait,WaitAsync和Release方法时不强制执行线程或任务标识
换句话说,该类不会查看哪个线程正在调用它。它只是一个简单的计数器。同一个线程可以多次获取信号量,这与多个线程获取信号量的情况相同。如果剩余的线程数减少到0,那么即使一个线程已经获得了该线程的信号量,如果它调用Wait()
,它将阻塞直到其他线程释放信号量。
因此,对于async
/ await
,await
可能会或可能不会在启动它的同一个线程中恢复这一事实并不重要。只要您保持Wait()
和Release()
来电平衡,它就会像您希望的那样有效。
在您的示例中,您甚至会异步等待信号量,因此不会阻塞任何线程。这很好,因为否则你第二次按下按钮就会使UI线程死锁。
相关阅读:
Resource locking between iterations of the main thread (Async/Await)
Why does this code not end in a deadlock
Locking with nested async calls
请注意有关重入/递归锁定的特别警告,尤其是async
/ await
。线程同步本身就很棘手,而这种困难是async
/ await
旨在简化的难点。在大多数情况下,它确实如此显着。但是当你将它与另一个同步/锁定机制混合时不会。