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

标签 c# race-condition

附加问题

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

原始问题

考虑以下代码片段,_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) 是否确保从缓存中刷新读取和写入?

最佳答案

does the _semaphore has the same effect as lock?

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

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

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

此外,SemaphorSlim 是一次性对象,因此您应该考虑在您的Calculate 类中实现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?

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

关于c# - SemaphoreSlim(1, 1) 是否确保像锁一样从缓存中刷新读取和写入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71638590/

相关文章:

c# - 无法将类型 'System.Drawing.Image' 转换为 'System.Drawing.Icon'

c# - Asp .Net Core 中的自定义模型绑定(bind)

c# - 将邮件编码为 : uri

java - Java 中的 volatile 和线程安全

concurrency - 在 Clojure 中是否有可能使用代理导致死锁(或其他坏情况)?

c# - 将旧的 VS 2002 Windows 窗体项目转换为新版本

c# - C# 中的缩放图像

java - Java 信号量的确定性如何保证?

c - 是否有任何工具可以在运行时检测竞争条件并重写代码以避免将来出现它们

javascript 真的很奇怪的行为