侧边栏壁纸
博主头像
Komi博主等级

WizMan Komi

  • 累计撰写 30 篇文章
  • 累计创建 43 个标签
  • 累计收到 3 条评论

目 录CONTENT

文章目录

C#中多线程情况下的lock以及Semaphore

Komi
2022-09-09 / 0 评论 / 0 点赞 / 53 阅读 / 2,324 字
温馨提示:
内容仅供参考,实际使用需根据自身条件进行调整与删改

何为lock?

MSDN:lock关键字的说明

Lock关键字用于将一个对象(object)隔离于其他的线程,并执行代码块中的内容而不会被其他线程中断,这段代码为互斥段,互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待,等到这段代码执行结束之后则其他线程可以来访问.

lock哪些变量?

1.为什么不可以是值类型?
在C#中,lock本质上是进入Moniter.Enter()方法中,而每次进入这个方法后会将传入的类型进行装箱操作(值类型->引用类型),那肯定,每次的地址引用一定是不同的,这就导致了在同一时间,别的线程照样能够访问里面的代码,达不到同步的效果
2.如果是使用字符串呢?
使用字符串实际上是不安全的,因为这段字符串是可以被更改的,这意味着整个程序中任何给定字符串都只有一个实例,如果在别的地方修改这个变量的话,内存地址会有改变,这可能会导致线程上的隔离失败,同样达不到同步的效果
3.比较推荐的lock的对象

private static readonly object obj = new object();

这里为什么要设置成只读的呢?这时因为如果在lock代码段中改变obj的值,其它线程就能直接访问了,因为引址的更改,object.ReferenceEquals必然会返回false

何为Semaphore?

MSDN:Semaphore类的说明

Semaphore实际上是用于允许线程执行的信号池子,池子中放入多少个信号就允许多少线程同时执行
Semaphore常用的方法有两个,一个是WaitOne()另一个是Release(),Release()的作用是退出信号量并返回前一个计数,而WaitOne()则是阻止当前线程,直到当前线程的WaitHandle收到信号

如何使用Semaphore类?

// x:初始线程数 y:最大的线程数
Semaphore sema = new Semaphore(x,y);

其中WaitOne()方法有一个重写的方法WaitOne(int millisecondsTimeout),这里可以直接指定超时时间来防止过长时间导致的线程阻塞

    private static Semaphore sema;

    public static void Main(string[] args)
    {
        Console.WriteLine("StartNow!");
        sema = new Semaphore(1, 5);

        foreach (var _ in Enumerable.Range(0, 10))
        {
            var tr = new Thread(pop);
            tr.Start();
        }
    }

    private static void pop()
    {
        sema.WaitOne();

        Console.WriteLine($"{Environment.CurrentManagedThreadId}进入休闲区域");
        Console.WriteLine($"{Environment.CurrentManagedThreadId}进入工作区域");
        Console.WriteLine($"{Environment.CurrentManagedThreadId}结束了");

        sema.Release();
    }

这里需要注意semaphore在最后一定需要去调用Release()方法来释放当前线程,否则会一直占用这个信号位! 尽量把代码块放在try{}finally{}中

SemaphoreSlim是什么鬼?

SemaphoreSlim类是对可同时访问资源或资源池的线程数加以限制的 Semaphore 的轻量替代

SemaphoreSlim的大部分功能与Semaphore类是相同的,只不过等待的时间会更短一些,详见这里.
同时,SemaphoreSlim可以支持异步等待的功能,如下代码

    private static SemaphoreSlim sema;

    public static void Main(string[] args)
    {
        Console.WriteLine("StartNow!");
        sema = new SemaphoreSlim(1, 1);
        Task.WaitAll(Enumerable.Range(0, 5)
            .Select(_ => Task.Run(delegate
                {
                    Console.WriteLine($"{Environment.CurrentManagedThreadId}进入休闲区域");
                    pop();
                    Console.WriteLine($"{Environment.CurrentManagedThreadId}Wooooo!!!");
                })).ToArray());

    }

    private static async void pop()
    {
        await sema.WaitAsync();
        Console.WriteLine($"{Environment.CurrentManagedThreadId}进入工作区域");
        Console.WriteLine($"{Environment.CurrentManagedThreadId}结束了");
        sema.Release();
    }
/**
* StartNow!
* 7进入休闲区域
* 12进入休闲区域
* 6进入休闲区域
* 8进入休闲区域
* 9进入休闲区域
* 9进入工作区域
* 9结束了
* 9Wooooo!!!
* 12Wooooo!!!
* 7Wooooo!!!
* 8Wooooo!!!
* 6Wooooo!!!
* 9进入工作区域
* 9结束了
* 6进入工作区域
* 6结束了
*/
0

评论区