.NET组件程序设计 第8章 多线程和并发管理 同步线程_监视器

本文深入解析.NET组件在多线程环境下的线程安全性问题,并详细介绍了如何通过监视器(Monitor)、泛型类型安全锁定、碎片锁定等同步机制确保对象的线程安全,以及结构体的线程安全实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

同步线程:

.NET组件(类)默认在一多线程环境执行,可通过并发多线程访问,但可能导致对象状态破坏。想要该对象线程安全,就应确保一次只有一个线程可访问该对象。

 

自动同步:

//&-&看不懂,后补

 

手动同步:.NET手动同步提供了丰富的同步锁

1.监视器(Monitor):设计成只和引用类型一同工作的锁。

private class MyClass
{
    private int m_Number;
    public int Number
    {
        get
        {
            //finally语句块在返回命令后执行,没必要使用临时变量保证线程安全了
            lock(typeof(MyClass)){ return m_Number; }
        }
    }

    //在方法内部封装Monitor促进松耦合和线程安全
    public void DoSomething()
    {
        //C#提供了lock语句实际编译为以下语句
        //lock(typeof(MyClass)){ ... }

        //请求锁并锁定对象
        //其他线程在请求该对象时,会被阻塞,直到调用Exit解除锁
        //多个未决线程会放入锁队列中,依次获得服务
        Monitor.Enter(typeof(MyClass));
        try
        {
            Thread.Sleep(3000);
            Console.Write(Thread.CurrentThread.Name + "线程执行完毕");
        }
        finally
        {
            //为对象解锁
            Monitor.Exit(typeof(MyClass));
        }
    }
}

//调用
MyClass mc = new MyClass();

Thread threadA = new Thread(mc.DoSomething);
threadA.Name = "threadA";

Thread threadB = new Thread(mc.DoSomething);
threadB.Name = "threadB";

threadA.Start();
threadB.Start();


 线程安全的结构:由于结构是值类型,那就不能把结构本身传递给Monitor,可以给结构中加以object类型成员变量,专用于Monitor。

//线程安全的结构
private struct MyStruct
{
    /* 用于锁的引用对象 */
    private object m_Lock;
    //这个字段是来打酱油的^-^
    private int m_Number;

    //构造函数
    public MyStruct(int number)
    {
        m_Lock = new object();
        m_Number = number;
    }

    public void DoSomething()
    {
        /* 因为结构不一定会调用构造函数初始化,需检查m_Lock是否为null */
        //Debug.Assert(m_Lock != null);
        lock(m_Lock)
        {
            Thread.Sleep(3000);
            Console.WriteLine(Thread.CurrentThread.Name + "线程执行完毕");
        }
    }

    public void DoSomething2()
    {
        /* 因为结构不一定会调用构造函数初始化,需检查m_Lock是否为null */
        //Debug.Assert(m_Lock != null);
        lock (m_Lock)
        {
            Console.WriteLine(Thread.CurrentThread.Name + "线程执行完毕");
        }
    }

    /* 深度拷贝 */
    //考虑到将当前结构 = 给另一结构,只会浅拷贝,对于结构中引用类型m_Lock会被多个结构共享
    //导致一个结构锁定,其他=的结构也都锁定
    //因此附加一深拷贝方法
    public MyStruct Clone()
    {
        MyStruct clone = new MyStruct(m_Number);
        return clone;
    }
}

//调用
MyStruct struct1 = new MyStruct(3);
//MyStruct struct2 = struct1;
MyStruct struct2 = struct1.Clone();

Thread threadA = new Thread(struct1.DoSomething);
threadA.Name = "threadA";

Thread threadB = new Thread(struct2.DoSomething2);
threadB.Name = "threadB";

threadA.Start();
//主线程挂起一定时间,为了保证threadA线程先锁定m_Lock对象
Thread.Sleep(100);
threadB.Start();

 

Monitor和泛型

//泛型类型安全锁定
public class MyClass<T>
{
    T m_T;
    public void DoSomething()
    {
        //因为泛型T可以为任何类型(值类型),除非限定T泛型类型
         //最好锁定整个对象
         lock(this)
        { ... }
    }
}


碎片锁定:按需将锁定代码和未锁定代码混搭,但该模式是一个非常沉重的负担,容易导致代码错误。

                    因避免碎片锁定,而在整个方法中锁定对象。

//碎片锁定
public void DoSomething()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(i);
    }

    lock (this)
    {
        Console.WriteLine("d");
    }
}


同步方法:相当于用一个lock语句包围整个方法。

using System.Runtime.CompilerServices;
//MethodImpl特性,也可以用于属性
[MethodImpl(MethodImplOptions.Synchronized)]
public void DoSomething()
{
    /* 代码 */
}

//以上等同于下列代码
//public void DoSomething()
//{
//       lock(this)
//       {
//           /* 代码 */
//       }
//}


Monitor等待和发送信号

wait()释放锁并等待另一个线程调用Monitor。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值