十二多线程

来源:互联网 发布:护考做题软件 编辑:程序博客网 时间:2024/04/28 23:59
 

多线程

信号驱动I/0和异步I/0的区别

信号驱动I/0是指进程预先告知内核,使得当某个描述字上发生某事时,内核使用信号通知相关进程。

异步I/0是进程执行I/0系统调用(比如读或者写),内核启动I/0操作后立刻返回进程,进程可以在I/0操作执行期间继续处理别的事情,然后当I/0操作成功或者失败时,内核以进程预先设定的方式通知进程。

使用锁保护同步数据的原则

多个线程使用同一个数据,必须保护该数据;

如果多个线程共用一个基础类型变量,应该声明为volatile,防止编译器使用将其缓存到寄存器内的优化方式;

共享的整数变量应该优先使用原子操作来修改其值;

有时候,可以考虑将某些需要共享的数据只让一个单独线程处理,然后将该线程作为一种服务提供给其他线程使用;

如果有很多数据需要在线程间共享,可以考虑使用数据库来管理这些数据。

不确定的情况下,使用锁来保护。

Readers/Writers

假设有很多线程共享一个变量,只有一个(或者很少的)线程负责修改这个变量的值,其他(或者大多数的)线程只是读取变量的值。如果采用常规思路,所有线程访问该变量都必须获得一个锁,这样的效率是很低的。因为如果写变量的线程没有执行对该变量的修改操作时,其他的读线程理论上都可以安全的读该变量的值。常规思路的解决方案让所有的读线程都排队,所以我们需要readers/writers锁来提供更好的效率。

readers/writers锁的解决方案里,只要没有任何线程在修改共享变量的值,所有其他线程都可以读取该变量的值,如果有一个线程正在修改共享变量的值,其他任何线程无论读取还是改写的操作都必须等待获得锁。这种解决方案适用于在写操作较少,读操作较多的多线程环境中。

readers/writers锁有两种策略,读优先或者写优先策略。读优先策略是指总是让读操作先完成,写优先是指只要有正在执行的或者等待的写操作,读操作就需要等待写操作完成以后才能执行。图书馆书籍查找系统通常使用读操作,而航班预定系统由于强调数据的有效性,采用写优先策略。

ACE提供了跨平台的两个类ACE_RW_Thread_MutexACE_RW_Process_Mutex

前者提供了线程间的同步,后者提供了进程范围的同步。这两个类采用了写优先策略。ACEreaders/writer锁仅适用于读操作较多而写操作较少的多线程环境,注意在大多数其他情况下,readers/writers锁都比采用mutex的常规思路慢。


信号量

(摘自http://blog.yesky.com/134/polaris0161/1555634.shtml)

信号量是一种Linux的资源,它可以让不同的进程间进行相互通信,因此它也被看作是IPC集中的一员。信号量的作用是在两个或多个进程访问公共资源集时 保持同步。

信号量是一个计数器的值,它可以被几个进程作为一个集合以原子的方式执行。信号量的计数器控制着对资源的访问控制,信号量提供了两个主要的操作来处理计数器的值:

(1)资源的使用者在使用资源之前等待信号量。如果信号量的值为0,则继续等待,如果大于0,则将信号量值减1,使用者开始使用资源。

(2)资源的使用者在资源使用完毕后通知信号量。使用者通知信号量不再使用资源了,信号量的值加1,检查等待信号量的使用者的序列,以确定是否有其他的使用者在等待之中。

使用信号量进行进程间的通信一般牵涉到以下操作:

相关信号量的操作函数一般应该包含下面的头文件;

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/sem.h>

1.创建信号量:在程序使用信号量之前,必须首先创建信号量。创建信号量的函数原形如下所示:

  int semget(key_t key,int nsems,int semflg);

参数说明:

(1)key:在本地系统中表示要创建或者访问的信号量集的关键字,当然为了避免与其他的信号量产生冲突,我们可以简单的利用IPC_PRIVATE来表示一个新建的信号量。

(2)nsems:要创建或者要访问的信号量集中信号量的数目。

(3)指定不同的选项和权限位的标志。可以为IPC_CREATE,IPC_EXCL.

创建一个新的信号量:

int semid

semid=semget(0x1234,2,IPC_CREATE|IPC_EXCL|0600);

if(semid<0)

{

  process error case.

}else

{

    do the next thing;

}

使用已经存在的信号量:

int semid;

semid=semget(0x1234,0,0);

if(semid<0)

{}else

{}

2.初始化信号集:

int semctl(int semid,int semnum,int cmd,union semun arg);

union semun

{

    int val;

    struct semid_ds *buf;

    ushort *array;

};

eg:

int semid;

int z;

union semun arg;

ushort initv[]={4,2};

 

arg.array=initv;

z=semctl(semid,2,SETALL,arg);

3.等待通知信号量:等待信号量和通知信号量都是利用函数semop(),只是相关的参数不同而已。

int semop(int semid,struct sembuf *sops,unsigned nsops);

struct sembuf

{

   short sem_num;  /*信号量的索引*/

   short sem_op; /*信号量的相关操作为一个整数(正数表示通知信号量,负数表示等待信号量)*/

   short sem_flg;/*相关的操作标记*/

};

eg

int z;

static struct sembuf sops[]=

{

  {1,-1,0},

  {0,-1,0}

};

z=semop(semid,sops,2);

如果是通知信号量,则sembuf的值可以设为

static struct sembuf sops[]=

{

   {1,+1,0},

   {0,+1,0}

};

z=semop(semid,sops,2);

4.删除信号量

   当信号量在系统中没有用处后,应该将其删除,这样才能释放内核中的支持表所占用的资源。

  int semctl(int semid,int semnum,int cmd,union semun arg);

eg:

int semid;

int z;

union semun arg;

z=semctl(semid,0,IPC_RMID,arg);

值得一提的是信号量的使用遵循自愿的原则的,也就是说信号量并不能强制的限制应用程序对资源的访问,我们在编写每一个程序的时候,都将通过资源的原则来执行如下操作:

1)在访问受控资源前等待信号量。

2)释放受控的资源后要通知信号量。

原创粉丝点击