额外问题
即使task1
和task2
在两个不同的内核上运行,SemaphoreSlim(1, 1)
是否仍然确保我有正确的输出1000000?
原始问题
考虑到以下代码片段,_semaphore
是否具有与lock
相同的效果?
输出:
- 使用锁:1000000
_semaphore = new SemaphoreSlim(1, 1)
:1000000(我运行了10次程序,结果相同(_semaphore = new SemaphoreSlim(10, 10)
:从999998到999990到1000000不等,等等
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Calculate
{
private int _val = 0;
private object _addLock = new object();
private SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
public int Val
{
get
{
return _val;
}
}
public void IncreManyTimes()
{
for (int i = 0; i < 500000; ++i)
{
//lock(_addLock)
//{
// _val = _val + 1;
//}
_semaphore.Wait();
_val = _val + 1;
_semaphore.Release();
}
}
}
class Program
{
static void Main(string[] args)
{
Calculate calculate = new Calculate();
Task.Run(() =>
{
Task task1 = Task.Run(() =>
{
calculate.IncreManyTimes();
});
Task task2 = Task.Run(() =>
{
calculate.IncreManyTimes();
});
Task.WaitAll(task1, task2);
}).Wait();
Console.WriteLine(calculate.Val);
Console.ReadKey();
}
}
}
相关问题:锁定是否确保从缓存中清除读写?如果是,如何?
我的问题可以描述为:SemaphoreSlim(1, 1)
是否确保从缓存中刷新读写?
_semaphore
是否具有与lock
相同的效果?
是和否。考虑到IncreManyTimes
方法的行为,它确实具有相同的效果(_val = _val + 1;
不会由多个线程同时运行(。
不过,他们也有一些不同。例如,lock
总是专用于其执行线程,而SemaphoreSlim
可以从任何线程释放。这就是为什么SemaphoreSlim
是围绕包含await
的代码块"锁定"的一个很好的选择(它甚至不会使用lock
编译,因为如果在不同的线程上执行延续,它将无法按预期工作(。
需要进一步注意的是,如果lock
内部存在异常,则锁定将被释放。为了达到同样的效果,您必须将_semaphore.Release();
放入finally
块中。
此外,SemaphorSlim
是一个一次性对象,因此您应该考虑在Calculate
类中实现IDisposable
。
最后,我不知道这是否只是一个简化的例子,但在这里你不需要任何这些。您可以简单地按Interlocked.Increment(ref _val)
递增,这不需要锁定。
编辑:
即使
task1
和task2
在两个不同的cpu内核上运行,SemaphoreSlim(1, 1)
是否仍能确保我有正确的输出1000000?
是的,因为它一次只允许关键部分中的一个线程。
信号量限制中的参数指定可以同时操作的线程数以及初始计数。如果您想使用信号量限制作为c#锁语句的等价物,那么支持的线程数必须为一。当你用10初始化你的sempahore时,你说最多有10个线程可以同时执行,这与锁不同。
信号量与刷新读写无关,这完全取决于信号量的使用方式。
如果要将其用作锁,则需要确保每次等待信号量时,它也会被释放,因此请使用try/finaly块。