首页 > 编程语言 >C#线程同步的方案

C#线程同步的方案

时间:2022-09-22 18:48:38浏览次数:41  
标签:同步 Console Monitor C# ++ num 线程 stopwatch obj

目录

需求

对一个数进行循环累加得到结果。

如果数值是0,循环1000000,我们期望的结果是应该等于1000000,但多线程执行累加,如果不进行处理就不能得到我们想要的结果。

处理方案

  • lock 关键字
  • Monitor
  • SpinLock 自旋锁
  • SemaphoreSemaphoreSlim 信号量
  • Interlocked 原子操作

先来看执行结果:
执行结果

可以看到除了第一个,其它方案按预期执行了。

实现代码

  • 第一个输出的代码,输出结果不是我们想要的
public static void Main()
{
    Console.WriteLine("Hello World!\n");

    long count = 1000000;
    long num = 0;
    object obj = new object();
    SpinLock spin = new SpinLock();
    SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
    Stopwatch stopwatch = new Stopwatch();

    //并行累加
    stopwatch.Start();
    num = 0;
    Parallel.For(0, count, i =>
    {
        num++;
    });
    stopwatch.Stop();
    Console.WriteLine($"并行累加\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");
}
  • lock 关键字
//lock
lock (obj)
{
    num++;
}
  • Monitor
//Monitor
Monitor.Enter(obj);
try
{
    num++;
}
finally
{
    Monitor.Exit(obj);
}
  • SpinLock 自旋锁
//自旋锁 SpinLock
bool getLock = false;
spin.Enter(ref getLock);
try
{
    num++;
}
finally
{
    if (getLock)
        spin.Exit();
}
  • SemaphoreSemaphoreSlim 信号量
//使用信号量 SemaphoreSlim
semaphoreSlim.Wait();
num++;
semaphoreSlim.Release();
  • Interlocked 原子操作
//使用原子操作
Interlocked.Increment(ref num);
  • 完整代码
using System.Diagnostics;

namespace boby.Synchronization
{
    internal class Program
    {
        public static void Main()
        {
            Console.WriteLine("Hello World!\n");

            long count = 1000000;
            long num = 0;
            object obj = new object();
            SpinLock spin = new SpinLock();
            SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
            Stopwatch stopwatch = new Stopwatch();

            //并行累加
            stopwatch.Start();
            num = 0;
            Parallel.For(0, count, i =>
            {
                num++;
            });
            stopwatch.Stop();
            Console.WriteLine($"并行累加\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");

            //lock
            stopwatch.Start();
            num = 0;
            Parallel.For(0, count, i =>
            {
                lock (obj)
                {
                    num++;
                }
            });
            stopwatch.Stop();
            Console.WriteLine($"lock\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");

            //Monitor
            stopwatch.Start();
            num = 0;
            Parallel.For(0, count, i =>
            {
                Monitor.Enter(obj);
                try
                {
                    num++;
                }
                finally
                {
                    Monitor.Exit(obj);
                }
            });
            stopwatch.Stop();
            Console.WriteLine($"Monitor\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");

            //自旋锁 SpinLock
            stopwatch.Start();
            num = 0;
            Parallel.For(0, count, i =>
            {
                bool getLock = false;
                spin.Enter(ref getLock);
                try
                {
                    num++;
                }
                finally
                {
                    if (getLock)
                        spin.Exit();
                }
            });
            stopwatch.Stop();
            Console.WriteLine($"自旋锁\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");

            //使用信号量 SemaphoreSlim
            stopwatch.Start();
            num = 0;
            Parallel.For(0, count, i =>
            {
                semaphoreSlim.Wait();
                num++;
                semaphoreSlim.Release();
            });
            stopwatch.Stop();
            Console.WriteLine($"信号量\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");

            //使用原子操作
            stopwatch.Start();
            num = 0;
            Parallel.For(0, count, i =>
            {
                Interlocked.Increment(ref num);
            });
            stopwatch.Stop();
            Console.WriteLine($"原子操作\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");

            ////使用内存屏障
            //stopwatch.Start();
            //num = 0;
            //Parallel.For(0, count, i =>
            //{
            //    Interlocked.MemoryBarrier();
            //    num++;
            //    Interlocked.MemoryBarrier();
            //});
            //stopwatch.Stop();
            //Console.WriteLine($"结果:{ num }, 内存屏障耗时:{ stopwatch.ElapsedMilliseconds }");
        }
    }
}

总结

  1. lock 关键字本质是语法糖,它等价于下面的代码:

    Monitor.Enter(obj);
    try
    {
        //同步的操作
    }
    finally
    {
        Monitor.Exit(obj);
    }
    

    当然 Monitor 类还支持其它功能,如

标签:同步,Console,Monitor,C#,++,num,线程,stopwatch,obj
From: https://www.cnblogs.com/peng-jy/p/16720450.html

相关文章

  • CSP 202104_2
    CSP202104_2目录CSP202104_2题目思路Code题目邻域均值思路CSP一贯风格,纯暴力一眼可见的70pts二维前缀和,没什么要说的Code#include<bits/stdc++.h>usingnamespac......
  • sysbench: error while loading shared libraries: libpq.so.5: cannot open shared o
    背景我的环境有MySQL、PostgreSQL数据库,为了压测方便我安装了sysbench[root]#gitclonehttps://github.com/akopytov/sysbench.git[root]#cdsysbench[root]#shau......
  • 利用Dockerfile文件和docker 命令 生成docker镜像
     一、sprinboot部分1、springboottest的springboot项目,写了controller测试importorg.springframework.beans.factory.annotation.Value;importorg.springframewor......
  • JavaScript 类(class)
    使用class关键字关键一个类,每个类中包含了一个特殊的方法 constructor(),它是类的构造函数创建类classClassName{constructor(name,url){this.name=name;this,url=u......
  • 【asp.net】background属性
    一、更改元素背景色可以为所有元素设置背景色,这包括body一直到em和a等行内元素。1、元素背景色p{background-color:gray;}2、背景色从元素中的文本向外延伸......
  • cannot be cast to java.util.Map
    特别记一下,对象的字段,首字母小写!!!代码:@OverridepublicList<MODEL_Vo>find(Stringstr){Stringsql="select*fromtab";Queryquery=em.......
  • CodeTON Round 2 (Div. 1 + Div. 2) - E. Count Seconds
    思维+DP[Problem-E-Codeforces](https://codeforces.com/contest/1695/problem/D2)题意给一张有\(n\)个结点\(m\)条有向边的有向无环图,\(1<=n,m<=1000\),每......
  • Java流程控制02(Scanner进阶)
    Scanner进阶使用判断输入的是否为整数:packageScanner;importjava.util.Scanner;publicclassDemo03{publicstaticvoidmain(String[]args){S......
  • Spring MVC框架:第十五章:多IOC容器整合
    多IOC容器整合SSM整合方式Spring、SpringMVC、MyBatisSpringMVC的核心Servlet会启动一个IOC容器,而ContextLoaderListener也会启动一个IOC容器。web.xml<?xmlversion......
  • 自定义loader使用说明,以APICloud平台为例
    一、应用自定义loader的作用一直以来,官方发布的AppLoader,只包含了官方模块。而其他开发者的自定义模块、付费模块、第三方SDK模块等都并未加入到loader中,这给开发者在开发......