SemaphoreSlim(1, 1) 是否确保像锁一样从缓存中刷新读取和写入?

Does SemaphoreSlim(1, 1) ensures read and writes are flushed from caches like lock?

附加问题

即使 task1task2 运行 在 2 个不同的内核上,SemaphoreSlim(1, 1) 是否仍然确保我有正确的输出 1000000?

原问题

考虑以下代码片段,_semaphore是否与lock具有相同的效果?

输出:

  1. 使用锁:1000000
  2. _semaphore = new SemaphoreSlim(1, 1): 1000000(我运行程序10次结果一样)
  3. _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();
        }
    }
}

相关问题:Does locking ensure reads and writes are flushed from caches? If so, how?

我的问题可以描述为:SemaphoreSlim(1, 1) 是否确保从缓存中清除读取和写入?

semaphoreslim中的参数指定了多少个线程可以并发运行,初始计数是多少。如果要使用 semaphoreslim 作为 c# lock 语句的等价物,那么支持的线程数 必须 为一个。当你用 10 初始化你的 sempahore 时,你是说最多 10 个线程可以同时执行,这与锁不同。

信号量与刷新读取或写入无关,这完全取决于您如何使用信号量。

如果你打算将它用作锁,你需要确保每次等待信号量时,它也会被释放,所以使用 try/finally 块。

does the _semaphore has the same effect as lock?

是也不是。考虑到您的 IncreManyTimes 方法的行为,它确实具有相同的效果(_val = _val + 1; 不会被多个线程同时 运行)。

不过,它们有一些不同。例如,lock 始终专用于其执行线程,而 SemaphoreSlim 可以从任何线程释放。这就是为什么 SemaphoreSlim 是 'locking' 围绕包含 await 的代码块的一个很好的替代方案(它甚至不会用 lock 编译,因为它不会按预期工作如果延续在不同的线程上执行)。

进一步说明,如果lock内部出现异常,则锁被释放。要达到相同的效果,您必须将 _semaphore.Release(); 放在 finally 块中。

此外,SemaphorSlim 是一次性对象,因此您应该考虑在 Calculate class.

中实施 IDisposable

最后,我不知道这是否只是一个简化的示例,但在这里您不需要任何这些。您可以简单地增加 Interlocked.Increment(ref _val),这不需要锁定。

编辑:

Does SemaphoreSlim(1, 1) still ensures that I have the correct output 1000000, even if task1 and task2 run on 2 different cpu cores?

是的,因为它一次只允许临界区中有一个线程。