Linux多线程及临界区编程例解

先贴示范代码:

// --------------------tmutex.h开始------------------------------
// 实现linux的互斥量c++封装

#ifndef TMUTEX_H
#define  TMUTEX_H

#include 
< pthread.h >

// 线程互斥量
struct  ThreadMutex
{
        ThreadMutex()
        
{
                pthread_mutex_init(
&mtx,NULL);
        }


        
~ThreadMutex()
        
{
                pthread_mutex_destroy( 
&mtx );
        }


        inline 
void lock()
        
{
                pthread_mutex_lock( 
&mtx );
        }


        inline 
void unlock()
        
{
                pthread_mutex_unlock( 
&mtx );
        }


        pthread_mutex_t mtx;

}
;                                                            
                                                              
// 空互斥量,即调用lock时什么事都不做。                                        
struct  NullMutex                                              
{                                                             
        inline 
void lock()                                    
        
{                                                     
        }
                                                     
        inline 
void unlock()                                  
        
{                                                     
        }
                                                     
}
;                                                            

template
< class  T >                                              
class  CAutoGuard                                              
{                                                             
public:                                                       
        CAutoGuard(T 
&mtx) : m_mtx(mtx)                       
        
{                                                     
                m_mtx.
lock();                                 
        }
                                                     
        
~CAutoGuard()                                         
        
{                                                     
                m_mtx.unlock();                               
        }
                                                     
protected:                                                    
        T 
&m_mtx;                                             
}
;                                                            
                                                              
#define  AUTO_GUARD( guard_tmp_var, MUTEX_TYPE, mtx )         
        CAutoGuard
< MUTEX_TYPE >  guard_tmp_var(mtx)             
#endif

// -------------------------tmutex.h结束------------------------------------------

// -------------------------主程序文件test.cpp开始----------------------------------

#include 
< pthread.h >
#include 
" tmutex.h "
#include 
< iostream >
using   namespace  std;

typedef ThreadMutex MUTEX_TYPE;    
// 使用线程互斥量的互斥量类型
// typedef NullMutex MUTEX_TYPE;         // 不使用互斥量的互斥量类型

MUTEX_TYPE g_mtx;            
// 互斥量变量定义

void   * print_msg_thread( void   * parg);

void   * print_msg_thread( void   * parg)
{//工作线程,用循环模拟一个的工作。
        char *msg = (char *)parg;

        AUTO_GUARD( gd, MUTEX_TYPE, g_mtx );
        
for(int i=0; i<10; i++ )
        
{
                cout 
<< msg << endl;
                sleep( 
1 );
        }

        
return NULL;
}


int  main()
{
        pthread_t t1,t2;

        
//创建两个工作线程,第1个线程打印10个1,第2个线程打印10个2。
        pthread_create( &t1, NULL, &print_msg_thread, (void *)"1" );       
        pthread_create( 
&t2, NULL, &print_msg_thread, (void *)"2" );        
        
         
//等待线程结束                                             
        pthread_join( t1,NULL);                               
        pthread_join( t2,NULL);                               
                                                              
        
return 0;                                             
}
    

// -----------------------------主程序文件test.cpp结束

 

    看了上面的示例代码及注释,相信已经了解该代码的功能。我们在主程序中创建两个线程,第1个线程循环打印10个1,第2个线程循环打印10个2。由于线程的特性,两个线程并不一定会按顺序执行,它们可能会被轮流调度执行。

    如果两个线程被轮流调度执行,那么所打印的10个1和10个2的排列顺序则不固定。线程1打印了几个字符后,可能会别打断,CPU被分配到线程2上去执行。这样可以尽可能让每个线程都得到CPU资源。但是另一方面也带来了问题。如果两个线程共同访问了一个变量。并且两个线程都会修改它,在修改未完成被打断的话,会使得最后修改的结果和预期的不一致。对于不能被打断的操作我们叫它原子操作。为了能使线程中的某段代码成为原子操作,我们就得使用互斥量。如本例所示的打印10个字符,如果我们不使用互斥量那么这个打印顺序就会被破坏,使用了互斥量后,线程1未离开互斥量所管的区域,线程2是不能再次进入的。这就保证了打印过程的原子操作性。

    Linux中使用临界区加锁的方法是用pthread_mutex_t进行操作,分别调用pthread_mutex_init、 pthread_mutex_destroy创建和释放pthread_mutex变量,调用pthread_mutex_lock和 pthread_mutex_unlock进行加锁和解锁。其中pthread_mutex_init和pthread_mutex_destroy只要在最开始的时候和不用的时候各调用一次,pthread_mutex_lock和pthread_mutex_unlock则是在每次加锁和解锁时调用。要注意的是它们的调用必须一一对应。

    本例的互斥量使用了C++的构造和析构以及模板的特性进行封装,保证分配和释放、加锁和解锁的成对,使得互斥量的使用更加简单。加锁时只需一个语句:AUTO_GUARD( gd, MUTEX_TYPE, g_mtx ); 该语句是个宏,展开宏得到的代码是:CAutoGuard gd(g_mtx); CAutoGuard对象的构造和析构自动调用g_mtx的lock和unlock函数进行加锁解锁。而锁的类型就看MUTEX_TYPE的定义了。下面这两行是互斥量锁类型的定义:
typedef ThreadMutex MUTEX_TYPE;    //使用线程互斥量的互斥量类型
//typedef NullMutex MUTEX_TYPE;        //不使用互斥量的互斥量类型

    其中第1行的类型是ThreadMutex,我们看该struct的定义,在lock和unlock函数中分别调用了pthread_mutex_lock和pthread_mutex_unlock,这样就实现了资源的锁定和解锁。

    而第2行的类型是NullMutex,在该struct的定义中,lock和unlock函数都是空函数,没有执行任何锁定解锁操作。

    因此,将MUTEX_TYPE的类型改为ThreadMutex或NullMutex就可以实现使用或不使用互斥量的效果。

    将上述两个文件保存并编译:g++ tmutex.h test.cpp -lpthread -o test

    编译完输出test可执行文件。输入./test执行程序。下面是使用互斥量和不使用互斥量的执行结果:

使用互斥量:

[root@hjclinux sampthread]# g++ tmutex.h test.cpp -lpthread -o test
[root@hjclinux sampthread]# ./test
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2

将test.cpp中的MUTEX_TYPE定义改成typedef NullMutex MUTEX_TYPE再编译执行结果如下:

[root@hjclinux sampthread]# ./test
1
2
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1

由于线程调度的关系,可能每次执行打印出1和2的顺序都不一样。

你可能感兴趣的:(linux学与用)