Thread同步—锁、信号量

一、锁Monitor(控制每个线程之间通信的执行顺)
1. Monitor(控制线程间通信顺序)
说到锁的时候通常都会想到Lock,Lock确实减少了我们不必要的劳动并且让代码更可观,但是如果我们要进行控制每个线程之间通信的执行顺序,则必须使用原生类,这里要注意一个问题就是“锁住什么”的问题,一般情况下我们锁住的都是静态对象,我们知道静态对象属于类级别,当有很多线程共同访问的时候,那个静态对象对多个线程来说是一个,不像实例字段会被认为是多个。
这里主要介绍Monitor类主要是锁定的临界区中只允许让一个线程访问,其他线程排队等待
Monitor.Enter()获取指定对象上的排他(锁住指定对象在当前线程访问期间,其他对象无法访问)
Monitor.Wait()暂时的释放资源锁,然后该线程进入”等待队列“中,那么自然别的线程就能获取到资源锁
Monitor.Pulse()唤醒“等待队列”中的线程,那么当时被Wait的线程就重新获取到了锁
Monitor.Exit()释放指定对象上的排他锁(释放锁住的对象允许其他对象访问)
看如下代码:

static void Main(string[] args)
        {
            Thread thread1 = new Thread(new ThreadStart(Run1));
            thread1.Start();
            Thread thread2 = new Thread(new ThreadStart(Run2));
            thread2.Start();
            Console.ReadLine();
        }
public static void Run1()
        {
            Monitor.Enter(obj);
            Console.WriteLine("Hello,Merry,I am Jack");
            Monitor.Wait(obj);
            Console.WriteLine("I Love You Merry");
            Monitor.Pulse(obj);
            Monitor.Wait(obj);
            Console.WriteLine("that is good Meery");
            Monitor.Pulse(obj);
            Monitor.Exit(obj);
        }
        public static void Run2()
        {
            Monitor.Enter(obj);
            Console.WriteLine("Hello,Jack,I am Merry");
            Monitor.Pulse(obj);
            Monitor.Wait(obj);
            Console.WriteLine("I also Love You Jack");
            Monitor.Pulse(obj);
            Monitor.Wait(obj);
            Console.WriteLine("Yes Jack");
            Monitor.Exit(obj);
        }

允许结果:
这里写图片描述

  1. ReaderWriterLock(实现多用户读/单用户写同步)
    C#提供了System.Threading.ReaderWriterLock类以适应多用户读/单用户写的场景。该类可实现以下功能:如果资源未被写操作锁定,那么任何线程都可对该资源进行读操作锁定,并且对读操作锁数量没有限制,即多个线程可同时对该资源进行读操作锁定,以读取数据。
    用Monitor或Mutex进行同步控制的问题:由于独占访问模型不允许任何形式的并发访问,这样的效率总是不太高。许多时候,应用程序在访问资源时是进行读操作,写操作相对较少。为解决这一问题,C#提供了System.Threading.ReaderWriterLock类以适应多用户读/单用户写的场景。该类可实现以下功能:如果资源未被写操作锁定,那么任何线程都可对该资源进行读操作锁定,并且对读操作锁数量没有限制,即多个线程可同时对该资源进行读操作锁定,以读取数据。如果资源未被添加任何读或写操作锁,那么一个且仅有一个线程可对该资源添加写操作锁定,以写入数据。简单的讲就是:读操作锁是共享锁,允许多个线程同时读取数据;写操作锁是独占锁,同一时刻,仅允许一个线程进行写操作。
    看下面代码:
public class WirteReadLockHelper
    {
        //资源
        static int intCount = 0;
        //读、写操作锁
        static ReaderWriterLock rwl = new ReaderWriterLock();

        static void Main(string[] args)
        {
            //分别创建2个读操作线程,2个写操作线程,并启动
            Thread tr0 = new Thread(new ThreadStart(Read));
            Thread tr1 = new Thread(new ThreadStart(Read));
            Thread tr2 = new Thread(new ThreadStart(Write));
            Thread tr3 = new Thread(new ThreadStart(Write));
            tr0.Start();
            tr1.Start();
            tr2.Start();
            tr3.Start();
            System.Console.ReadKey();
        }

        //读数据
        static void Read()
        {

            //申请读操作锁,如果在1000ms内未获取读操作锁,则放弃
            rwl.AcquireReaderLock(1000);
            Console.WriteLine("开始读取数据,我读取的数据是:{0}", intCount);
            Thread.Sleep(10);
            Console.WriteLine("读取数据结束,我读取的数据是:{0}", intCount);
            //释放读操作锁
            rwl.ReleaseReaderLock();


        }

        //写数据
        static void Write()
        {
            //申请写操作锁,如果在1000ms内未获取写操作锁,则放弃
            rwl.AcquireWriterLock(1000);
            Console.WriteLine("开始写数据,我读取的数据是:{0}", intCount);
            //将theResource加1
            intCount++;
            Thread.Sleep(100);
            Console.WriteLine("写数据结束,我读取的数据是:{0}", intCount);
            //释放写操作锁
            rwl.ReleaseWriterLock();

        }
    }

运行结果:
这里写图片描述

观察运行结果,我们很容易看出:读操作锁是共享锁,允许多个线程同时读取数据;写操作锁是独占锁,仅允许一个线程进行写操作。

  如果一个线程在获取读操作锁后,进行读操作的途中,希望提升锁级别,将其变为写操作锁,可以调用ReaderWriterLock类的UpgradeToWriterLock(int timeOut)方法,该方法返回一个LockCookie值,该值保存了UpgradeToWriterLock方法调用前线程锁的状态。待写操作完成后,可调用DowngradeFromWriterLock(LockCookie lockcookie)方法,该方法根据传入的LockCookie参数值,将线程锁恢复到UpgradeToWriterLock方法调用前的状态。
  
二、信号量(Semaphore控制线程执行的数量)
信号量(Semaphore)是由内核对象维护的int变量,当信号量为0时,在信号量上等待的线程会堵塞,信号量大于0时,就解除堵塞。当在一个信号量上等待的线程解除堵塞时,内核自动会将信号量的计数减1。在.net 下通过Semaphore类来实现信号量同步。
Semaphore类限制可同时访问某一资源或资源池的线程数。线程通过调用 WaitOne方法将信号量减1,并通过调用 Release方法把信号量加Semaphore类中重要方法有:
Semaphore(int initialCount, int maximumCount)该方法是信号量的构造函数initialCount信号量初始化时默认能够允许执行的线程数量,maximumCount表示该信号量能够允许执行线程的最大数量
semaphore.WaitOne();当线程调用的方法中添加了该WaitOne(线程阻塞)的方法时,那么执行的线程数量超过信号量初始化时默认的线程数量时,超过的线程数量就会发生堵塞不在执行,当调用Release方法时传入的参数就是释放掉的信号量(也就是说信号量在初始化的时候默认执行线程的数量的基础上还能够允许执行的线程数量)
semaphore.Release(releaseCount) 当调用Release方法时传入的参数就是释放掉的信号量(也就是说信号量在初始化的时候默认执行线程的数量的基础上还能够允许执行的线程数量)
看下面代码:

// 初始信号量计数为5,最大计数为9  
      //Semaphore(int initialCount, int maximumCount)
       //initialCount表示该信号量默认执行线程数量
       public static Semaphore semaphore = new Semaphore(5,9);
       public static int time = 0;
       static void Main(string[] args)
       {
           for (int i = 0; i < 8; i++)
           {
               Thread test = new Thread(new ParameterizedThreadStart(TestMethod));

               // 开始线程,并传递参数  
               test.Start(i);
           }

           // 等待1秒让所有线程开始并阻塞在信号量上  
           Thread.Sleep(1000);

           // 信号量计数加2   
           // 最后可以看到输出结果次数为7次  
           semaphore.Release(2);
           Console.Read();
       }

       public static void TestMethod(object number)
       {
           // 设置一个时间间隔让输出有顺序  
           int span = Interlocked.Add(ref time, 100);
           Thread.Sleep(1000 + span);

           //信号量计数减1
           //方法放
           semaphore.WaitOne();

           Console.WriteLine("Thread {0} run ", number);
       }

允许结果:
这里写图片描述

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值