分享

linux多线程

 A_Geek 2013-12-31

一、多线程


        多线程相比多进程有很明显的不同:线程共享相同的内存空间,不同的线程可以存取内存中的同一个变量。所以,程序中的所有线程都可以读或写声明过的全局变 量。而fork()多进程,每个进程都使用独立的内存空间,虽然有很多的通信方法(共享内存,套接字,文件等等),但是这无疑增加了程序的复杂度。下面是 线程的基本操作:

1.创建线程


函数声明:
  1. int pthread_create (pthread_t * __newthread,  
  2.                 pthread_attr_t * __attr,  
  3.                 void *(*__start_routine) (void *),  
  4.                 void *__arg);  

返回值:
        若成功则返回0,否则返回出错编号,返回成功时,由__newthread指向的内存单元被设置为新创建线程的线程ID。__attr参数用于制定各种不 同的线程属性。新创建的线程从__start_routine函数的地址开始运行,该函数只有一个空指针参数arg,如果需要像start_rtn函数传 递的参数不止一个,那么需要把这些参数放到一个结构体中,然后把这个结构的地址作为arg的参数传入。

2.资源释放pthread_join


函数声明:
  1. int phread_join(pthead_t __th,void **_thread_return);  

        函数pthread_join用来等待一个线程的结束。第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返 会值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。如果执行成功,将返回0, 如果失败则返回一个错误号。

        在linux中,默认情况下是在一个线程被创建后,必须使用此函数对创建的线程进行资源回收,但是可以使线程为分离属性,来设置当一个线程结束时,直接回收此线程所占用的系统资源。设置分离属性函数:
  1. int pthread_detach(pthead_t __th);  

3.互斥锁pthread_mutex_t

        如果线程一在时间time时刻对全局变量tmp进行写操作,而同一时刻线程二对tmp进行读操作,如果两个线程是同优先级的情况,根据cpu分配给这两个 线程的时间片的不同,造成线程二中读到的tmp的值可能是线程一修改之前的值,也可能是线程一修改之后的值,这将非常危险。程序内是不允许出现这种情况 的,而锁就是保护共享资源,防止同一时刻不同线程读写同一段内存而存在的。

函数声明:
  1. //创建  
  2. int pthread_mutex_init(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr);  
  3. //注销  
  4. int pthread_mutex_destory(pthread_mutex_t *__mutex);  
  5. //加锁  
  6. int pthread_mutex_lock(pthread_mutex_t *__mutex);  
  7. //加锁(非阻塞)  
  8. int pthread_mutex_trylock(pthread_mutex_t *__mutex);  
  9. //解锁  
  10. int pthread_mutex_unlock(pthread_mutex_t *__mutex);  

1).创建和注销

两种方法创建互斥锁,静态方式和动态方式,静态方式:
  1. phread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER;  
在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

动态方式是采用pthread_mutex_init()函数来初始化互斥锁,方法如下:
  1. phread_mutex_t mutex;  
  2. pthread_mutex_init(&mutex, const NULL);  
其中mutexattr用于指定互斥属性(见下),如果为NULL则使用缺省属性。

pthread_mutex_destroy()用于注销一个互斥锁,方法如下:
  1. pthread_mutex_destory(&mutex);  
销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于非锁定状态。

2).互斥锁属性

        互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型试图对一个已经被锁定的互斥锁加锁时表现不通。有四个值可供选择:
*PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。

*PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。

*PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当步允许多次加锁 时不会出现最简单情况下的死锁。

*PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

3).锁操作

        锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型, 解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则EPERM;在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法在获 得锁。

phread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占用时返回EBUSY而不是挂起等待。

4.条件变量pthread_cond_t


        如果线程正在等待某个特定条件发生,它应该如何处理这种情况?它可以重复对互斥对象锁定和解锁,每次都会检查共享数据结,以查找某个值。但这是在浪费时间 和资源,而且这种繁忙查询的效率非常低。解决这个问题的最佳方法是使用pthread_cond_wait()调用来等待特殊条件发生。

相关函数:
  1. //初始化,属性忽略  
  2. int pthread_cond_init(pthread_cond_t * __cond, __const pthread_condattr_t * __cond_attr);  
  3. //注销  
  4. int pthread_cond_destroy(pthread_cond_t *__cond);  
  5. //阻塞等待  
  6. int pthread_cond_wait(pthread_cond_t * __cond, pthread_mutex_t * __mutex);  
  7. //超时等待  
  8. int pthread_cond_timedwait(pthread_cond_t *__cond,pthread_mutex_t *__mutex, const struct timespec *__time);  
  9. //唤醒指定cond  
  10. int pthread_cond_signal(pthread_cond_t *__cond);  
  11. //唤醒所有等待  
  12. int pthread_cond_broadcast();  

1).创建和注销

条件变量和互斥锁一样,都有静态、动态两种创建,静态方式如下:
  1. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  

动态方式调用pthread_cond_init()函数,方式如下:
  1. pthread_cond_t cond;  
  2. pthread_cond_init(&cond , NULL);  

注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。

2).等待和唤醒

        等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待如果 在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待;其中__time与time()系统调用是相同的绝对时间形式。

        无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或 pthread_cond_timedwait(),下同)的竞争条件。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP) 或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须给本线程加锁 (pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开 pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。

如下面的代码片段:
        线程一,唤醒线程二:
  1. //do something  
  2. if (condition == true) {  
  3.     pthread_cond_signal(&test_cond);  
  4. }  
        线程二,等待被唤醒:
  1. pthread_mutex_lock(&test_mutex);  
  2. pthread_cond_wait(&test_cond, &test_mutex);  
  3. //do something  
  4. pthread_mutex_unlock(&test_mutex);  

二、示例代码

1.运行环境

        Linux、gcc、终端(terminal)操作

2.编译、执行方式

编译方法:
  1. $ gcc server.c -o server -lpthread  
  2. $ gcc client.c -o client  

执行方法:
        打开2个终端,一个终端运行服务器端:
  1. $ ./server  
        
        一个终端运行客户端:
  1. $ ./client  

3.操作说明

        在运行client的终端中使用组合键 ctrl+c 时,服务器端打印:sigint_Action;使用组合键 ctrl+\ 时服务器端打印:sigquit_Action;可以反复输入2种组合键,每次输入服务器端都会打印相应信息,但应注意当连续输入3次组合键 ctrl+\ 时2个程序分别结束,这也是测试程序结束的方法。

4.信号部分说明

        使用命令:
  1. $ kill -l  
        可以看到信号列表,平时使用比较多的杀死进程,如:kill -9 PID ,实际上用的就是信号:9) SIGKILL,该信号的默认处理方式是杀死进程。而我们这里使用的信号是:SIGINT和SIGQUIT,触发方式就是组合键:ctrl+c 和 ctrl+\,并且不使用系统默认处理方式,而是自己处理信号。

编译、执行截图:
server端:


client端:


5.程序源码:

以下是服务器端程序:server.c
  1. #include <termios.h>  
  2. #include <stdio.h>  
  3. #include <unistd.h>  
  4. #include <stdlib.h>  
  5. #include <fcntl.h>  
  6. #include <string.h>  
  7. #include <netinet/in.h>  
  8. #include <arpa/inet.h>  
  9. #include <sys/socket.h>  
  10. #include <sys/types.h>  
  11. #include <pthread.h>  
  12.   
  13. typedef unsigned int U_16;  
  14. typedef char C_8;  
  15. typedef struct sockaddr SA;  
  16.   
  17. struct data {  
  18.     U_16 sigint_flag;       //非零执行  
  19.     U_16 sigquit_flag;      //非零执行  
  20.     U_16 overAll_flag;  //非零才判断,为零取消所有动作  
  21. }flags;  
  22.   
  23. int index2 = 0;  
  24. struct sockaddr_in s_addr;  
  25. pthread_mutex_t sigint_mutex;  
  26. pthread_mutex_t sigquit_mutex;  
  27. pthread_cond_t sigint_cond;  
  28. pthread_cond_t sigquit_cond;  
  29.   
  30. void *sigint_Action(void *arg)  
  31. {  
  32.     while(1) {  
  33.         pthread_mutex_lock(&sigint_mutex);  
  34.         pthread_cond_wait(&sigint_cond,&sigint_mutex);        
  35.         if(1 == index2) {  
  36.             printf("sigint_Action\n");  
  37.             pthread_mutex_unlock(&sigint_mutex);  
  38.         }  
  39.         if(2 == index2) {  
  40.             pthread_mutex_unlock(&sigint_mutex);  
  41.             return NULL;  
  42.         }  
  43.     }  
  44. }  
  45.   
  46. void *sigquit_Action(void *arg)  
  47. {  
  48.     while(1) {  
  49.         pthread_mutex_lock(&sigquit_mutex);  
  50.         pthread_cond_wait(&sigquit_cond,&sigquit_mutex);  
  51.         if(1 == index2) {  
  52.             printf("sigquit_Action\n");  
  53.             pthread_mutex_unlock(&sigquit_mutex);  
  54.         }  
  55.   
  56.         if(2 == index2) {  
  57.             pthread_mutex_unlock(&sigquit_mutex);  
  58.             return NULL;  
  59.         }  
  60.     }  
  61. }  
  62.   
  63. void sys_Init()  
  64. {  
  65.     pthread_t sigint_pthread;  
  66.     pthread_t sigquit_pthread;  
  67.   
  68.     if(pthread_create(&sigint_pthread,NULL,sigint_Action,NULL)) {  
  69.         printf("sigint_pthread create failed\n");  
  70.         exit(-1);  
  71.     }  
  72.   
  73.     if(pthread_create(&sigquit_pthread,NULL,sigquit_Action,NULL))   {  
  74.         printf("sigquit_pthread create failed\n");  
  75.         exit(-1);  
  76.     }  
  77.   
  78.     pthread_mutex_init(&sigint_mutex,NULL);  
  79.     pthread_mutex_init(&sigquit_mutex,NULL);  
  80.     pthread_cond_init(&sigint_cond,NULL);  
  81.     pthread_cond_init(&sigquit_cond,NULL);  
  82.   
  83.     return;  
  84. }  
  85.   
  86. int main(int argc,char *argv[])  
  87. {  
  88.     int server_fd,client_fd;  
  89.     socklen_t len;  
  90.     struct sockaddr_in c_addr;  
  91.   
  92.     if((server_fd=socket(PF_INET,SOCK_STREAM,0)) == -1) {  
  93.         perror("socket fail");  
  94.         exit(-1);  
  95.     }  
  96.   
  97.     memset(&s_addr,0,sizeof(s_addr));  
  98.     s_addr.sin_family = PF_INET;  
  99.   
  100.     if(argc < 3) {  
  101.         s_addr.sin_port = htons(8000);  
  102.         s_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  103.     }else   {  
  104.         s_addr.sin_port = htons(atoi(argv[2]));  
  105.         s_addr.sin_addr.s_addr = inet_addr(argv[1]);  
  106.     }  
  107.   
  108.     if((bind(server_fd,(SA *)&s_addr,sizeof(s_addr))) == -1)    {  
  109.         perror("action bind\n");  
  110.         exit(-1);  
  111.     }  
  112.   
  113.     if(listen(server_fd,10) == -1)  {  
  114.         perror("listen fail");  
  115.         exit(-1);  
  116.     }  
  117.   
  118.     len = sizeof(s_addr);  
  119.     if((client_fd=accept(server_fd,(SA *)&c_addr,&len)) == -1)  {  
  120.         perror("accept fail");  
  121.         exit(-1);  
  122.     }  
  123.     sys_Init();  
  124.     memset(&flags,0,sizeof(flags));  
  125.     while(1) {  
  126.         if(read(client_fd,&flags,sizeof(flags)) < 0) {  
  127.             perror("read error");  
  128.             exit(-1);  
  129.         }  
  130.         if(1 == flags.overAll_flag)     {  
  131.             if(1 == flags.sigint_flag)  {  
  132.                 index2 = 1;  
  133.                 pthread_cond_signal(&sigint_cond);  
  134.             }  
  135.             if(1 == flags.sigquit_flag) {  
  136.                 index2 = 1;  
  137.                 pthread_cond_signal(&sigquit_cond);  
  138.             }  
  139.         }else if(0 == flags.overAll_flag)   {  
  140.             printf("action process end\n");  
  141.             index2 = 2;  
  142.             close(client_fd);  
  143.             close(server_fd);  
  144.             return 0;  
  145.         }  
  146.     }  
  147. }  

以下是客户端程序:client.c
  1. #include <sys/stat.h>  
  2. #include <fcntl.h>  
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <string.h>  
  6. #include <sys/time.h>  
  7. #include <sys/types.h>  
  8. #include <unistd.h>  
  9. #include <netinet/in.h>  
  10. #include <arpa/inet.h>  
  11. #include <sys/socket.h>  
  12. #include <sys/types.h>  
  13. #include <signal.h>  
  14.   
  15. typedef unsigned int U_16;  
  16. typedef struct sockaddr SA;  
  17.   
  18. static int sockfd;  
  19. static socklen_t len;  
  20. static struct sockaddr_in s_addr;  
  21. static int g_end = 0;  
  22.   
  23. struct data {  
  24.     U_16 sigint_flag;       //非零执行  
  25.     U_16 sigquit_flag;      //非零执行  
  26.     U_16 overAll_flag;  //非零才判断,为零取消所有动作  
  27. }flags;  
  28.   
  29. void sighandler(int signo)  
  30. {  
  31.       
  32.     switch(signo) {  
  33.     case SIGINT:  
  34.         g_end = 0;  
  35.         flags.sigint_flag = 1;  
  36.         flags.sigquit_flag = 0;  
  37.         flags.overAll_flag = 1;  
  38.         if(write(sockfd,&flags,sizeof(flags)) < sizeof(flags))   {  
  39.             printf("write size error\n");  
  40.             exit(-1);  
  41.         }  
  42.         break;  
  43.     case SIGQUIT:  
  44.         g_end++;  
  45.         flags.sigint_flag = 0;  
  46.         flags.sigquit_flag = 1;  
  47.         flags.overAll_flag = 1;  
  48.         if(3 == g_end) {  
  49.             flags.overAll_flag = 0;  
  50.         }  
  51.         if(write(sockfd,&flags,sizeof(flags)) < sizeof(flags))   {  
  52.             printf("write size error\n");  
  53.             exit(-1);  
  54.         }  
  55.         break;  
  56.     }  
  57. }  
  58.   
  59. int main(int argc,char *argv[])  
  60. {  
  61.       
  62.     memset(&flags,0,sizeof(flags));  
  63.     memset(&s_addr,0,sizeof(s_addr));  
  64.     s_addr.sin_family = PF_INET;  
  65.   
  66.     if(argc < 3) {  
  67.         s_addr.sin_port = htons(8000);  
  68.         s_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  69.     }else   {  
  70.         s_addr.sin_port = htons(atoi(argv[2]));  
  71.         s_addr.sin_addr.s_addr = inet_addr(argv[1]);  
  72.     }  
  73.   
  74.     if((sockfd=socket(PF_INET,SOCK_STREAM,0)) == -1)    {  
  75.         perror("socket fail");  
  76.         exit(-1);  
  77.     }  
  78.       
  79.     if(connect(sockfd,(SA *)&s_addr,sizeof(s_addr)) == -1)  {  
  80.         perror("connect fail");  
  81.         exit(-1);  
  82.     }  
  83.       
  84.     while(1) {  
  85.         signal(SIGINT,sighandler);  
  86.         signal(SIGQUIT,sighandler);  
  87.         if(3 == g_end) {  
  88.             close(sockfd);  
  89.             exit(0);  
  90.         }  
  91.         sleep(1);  
  92.     };  
  93.       
  94.     return 0;  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多