Mutex类及它的兄弟

转帖:http://www.cnblogs.com/1-2-3/articles/1212391.html

摘要

1-2-3 翻开那《葵花宝典》,只见页首赫然写着几个大字:“欲练神功,必先自宫”,旁边几行歪歪扭扭的小字,又不知是哪位前辈高人所写:“在WC里占蹲位的3种方 法:1. 如果你只对某个蹲位情有独钟,就要WaitOne(),但是不要忘了ReleaseMutex(),千万别WaitOne()两次只 ReleaseMutex()一次(你干这种占着MK不LS的事,憋坏了后来的小朋友怎么办?就算没有小朋友,憋坏了小猫小狗也不好啊……);2. 如果你喜欢讲排场,需要占2个蹲位才肯办事,则要WaitAll([蹲位1, 蹲位2]);3. 如果你觉得随便去哪个蹲位办事都无所谓,那就可以WaitAny([蹲位1, 蹲位2])……”。

Mutex的WaitOne()函数

前几天1-2-3去黑木崖找东方不败玩,听到东方不败抱怨说整天绣花眼睛好累呀,于是1-2-3就给东方不败编了一个活动眼睛的程序。

class  Program
{
    
static   void  Main( string [] args)
    {
        
//  为截图方便把窗体设小一点
        Console.WindowWidth  =   30 ; Console.BufferWidth  =   30
        Console.WindowHeight 
=   16 ; Console.BufferHeight  =   16 ;
        
        Mutex mk 
=   new  Mutex( false " my mutex " );
        
for  ( int  i  =   0 ; i  <   1000 ; i ++ )
        {
            mk.WaitOne();
            
for  ( int  j  =   0 ; j  <   30 ; j ++ )
            {
                Console.Write(
" > " );
                Thread.Sleep(
100 );
            }
            mk.ReleaseMutex();
            Thread.Sleep(
500 );
        }
    }
}


接连运行此程序的两个实例,把它们并排排放在一起(如下图所示),即可看到箭头从左边的窗体“穿越”到右边窗体的效果了。


是的,我们需要同步两个进程(中的主线程),这个工作需要交给Mutex。Mutex和Monitor的概念十分相似,只不过Monitor是.net内 建的线程同步机制,Mutex是封装了Windows操作系统的线程同步机制;Monitor速度快,Mutex的速度要比Monitor慢很 多;Monitor只能用于同步同一进程内的线程;Mutex则可以用于同步隶属于不同进程的线程。

Mutex的WaitAll()函数

现在我们对WC进行了扩建,把mk增加到两个,可是却遇到了两个讲排场的进程,它们都要同时占两个mk才肯办事,所以运行起来的效果和前一个程序一样。

class  Program
{
    
static   void  Main( string [] args)
    {
        
//  为截图方便把窗体设小一点
        Console.WindowWidth  =   30 ; Console.BufferWidth  =   30
        Console.WindowHeight 
=   16 ; Console.BufferHeight  =   16 ;
        
        Mutex mk1 
=   new  Mutex( false " my mutex1 " );
        Mutex mk2 
=   new  Mutex( false " my mutex2 " );
        Mutex[] mks 
=   new  Mutex[] { mk1, mk2 };

        
for  ( int  i  =   0 ; i  <   1000 ; i ++ )
        {
            Mutex.WaitAll(mks);
            
for  ( int  j  =   0 ; j  <   30 ; j ++ )
            {
                Console.Write(
" > " );
                Thread.Sleep(
100 );
            }

            mk1.ReleaseMutex();
            mk2.ReleaseMutex();
            Thread.Sleep(
500 );
        }
    }
}




Mutex的WaitAny()函数

看下这个小程序

 1   class  Program
 2   {
 3        static   void  Main( string [] args)
 4       {
 5            //  为截图方便把窗体设小一点
 6           Console.WindowWidth  =   30 ; Console.BufferWidth  =   30
 7           Console.WindowHeight  =   16 ; Console.BufferHeight  =   16 ;
 8           
 9           Mutex mk1  =   new  Mutex( false " my mutex1 " );
10           Mutex mk2  =   new  Mutex( false " my mutex2 " );
11           Mutex[] mks  =   new  Mutex[] { mk1, mk2 };
12  
13            for  ( int  i  =   0 ; i  <   1000 ; i ++ )
14           {
15                int  index  =  Mutex.WaitAny(mks);  //  返回值为此进程占用的mk在mks里的index
16               Console.Write( " Index:  "   +  index.ToString());
17                for  ( int  j  =   0 ; j  <   30 ; j ++ )
18               {
19                   Console.Write( " > " );
20                   Thread.Sleep( 100 );
21               }
22  
23               mks[index].ReleaseMutex();
24               Thread.Sleep( new  Random().Next( 100 3000 ));
25           }
26       }
27   }


如果同时运行此程序的两个实例,正如本文摘要里所写的,只要mk1和mk2有一个是空闲的,进程就可以进去办事,所以两个进程可以同时输出">"字 符。注意程序的第24行,每个进程在输出30个">"字符后都会随机Sleep 100到3000毫秒,这样就有可能出现mk1和mk2同时空闲的情况,所以就会出现一会儿进程1占用mk1而进程2占用mk2;一会儿进程1占用mk2 而进程2占用mk1的情况(在下图分别用绿色和红色波浪线标出)。


EventWaitHandle、AutoResetEvent 和 ManualResetEvent

EventWaitHandle的名字与Mutex差了很多,不过它可是Mutex不折不扣的兄弟——它和Mutex都是WaitHandle的子类,用法也差不多。下面这两段程序实现了与本文的第一段程序相同的功能。


class  Program
{
    
static   void  Main( string [] args)
    
{
        
//  为截图方便把窗体设小一点
        Console.WindowWidth  =   30 ; Console.BufferWidth  =   30 ;
        Console.WindowHeight 
=   16 ; Console.BufferHeight  =   16 ;

        EventWaitHandle mk 
=   new  EventWaitHandle( true , EventResetMode.AutoReset,  " my mk " );
        
        
for  ( int  i  =   0 ; i  <   1000 ; i ++ )
        
{
            mk.WaitOne();
            
for  ( int  j  =   0 ; j  <   30 ; j ++ )
            
{
                Console.Write(
" > " );
                Thread.Sleep(
100 );
            }

            mk.Set();
            Thread.Sleep(
500 );
        }

    }

}

 


class  Program
{
    
static   void  Main( string [] args)
    
{
        
//  为截图方便把窗体设小一点
        Console.WindowWidth  =   30 ; Console.BufferWidth  =   30 ;
        Console.WindowHeight 
=   16 ; Console.BufferHeight  =   16 ;

        EventWaitHandle mk 
=   new  EventWaitHandle( true , EventResetMode.ManualReset,  " my mk " );
        
        
for  ( int  i  =   0 ; i  <   1000 ; i ++ )
        
{
            mk.WaitOne();
            mk.Reset();   
//  把这行去掉会咋样?
             for  ( int  j  =   0 ; j  <   30 ; j ++ )
            
{
                Console.Write(
" > " );
                Thread.Sleep(
100 );
            }

            mk.Set();
            Thread.Sleep(
500 );
        }

    }

}


AutoResetEvent 和 ManualResetEvent 是 EventWaitHandle 的子类,功能都差不多,就不多说了。

互斥锁(Mutex)是一种用于多线程程序中的同步机制,用来避免多个线程同时访问共享资源导致的数据竞争和不一致性问题。互斥锁的特点是同一时刻只有一个线程能够获得锁,从而访问临界区中的共享资源。 在C++标准库中,`std::mutex`是互斥锁的一个实现。它提供了一系列成员函数来控制对共享资源的访问: - `lock()`:锁定互斥锁,如果该互斥锁已被其他线程锁定,则当前线程将被阻塞,直到获取锁。 - `unlock()`:释放互斥锁,使得其他等待锁的线程可以获取到锁。 - `try_lock()`:尝试锁定互斥锁,如果锁可用,则锁定它并返回true,否则返回false,当前线程不会被阻塞。 `std::mutex`通常是通过RAII(资源获取即初始化)原则来使用的,这通常通过`std::lock_guard`或`std::unique_lock`这包装器来实现。这些包装器在构造函数中自动锁定互斥锁,在析构函数中自动解锁互斥锁,从而保证了即使在异常发生时也能正确释放锁。 以下是一个简单的`std::mutex`使用示例: ```cpp #include <mutex> #include <thread> std::mutex mtx; // 定义一个互斥锁 void print_even(int n) { for (int i = 0; i < n; ++i) { mtx.lock(); // 锁定互斥锁 if (i % 2 == 0) { std::cout << i << " "; } mtx.unlock(); // 解锁互斥锁 } } void print_odd(int n) { for (int i = 0; i < n; ++i) { mtx.lock(); if (i % 2 != 0) { std::cout << i << " "; } mtx.unlock(); } } int main() { std::thread t1(print_even, 10); std::thread t2(print_odd, 10); t1.join(); t2.join(); return 0; } ``` 在这个例子中,两个线程分别打印出0到9中的偶数和奇数,互斥锁确保同一时刻只有一个线程能够执行打印操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值