linux,pthread(转)
1.“线程”
进程与线程之间是有区别的,不过内核只提供了轻量进程的⽀持,未实现线程模型。Linux是⼀种“多进程单线程”的。Linux本⾝只有进程的概念,⽽其所谓的“线程”本质上在内核⾥仍然是进程。
⼤家知道,进程是资源分配的单位,同⼀进程中的多个线程共享该进程的资源(如作为共享内存的全局变量)。Linux中所谓的“线程”只是在被创建时clone了⽗进程的资源,因此clone出来的进程表现为“线程”,这⼀点⼀定要弄清楚。因此,Linux“线程”这个概念只有在打冒号的情况下才是最准确的。
⽬前Linux中最流⾏的线程机制为LinuxThreads,所采⽤的就是线程-进程“⼀对⼀”模型,调度交给核⼼,⽽在⽤户级实现⼀个包括信号处理在内的线程管理机制。LinuxThreads由Xavier Leroy (Xavier.Leroy@inria.fr)负责开发完成,并已绑定在GLIBC中发⾏,它实现了⼀种BiCapitalized⾯向Linux的Posix 1003.1c “pthread”标准接⼝。Linuxthread可以⽀持Intel、Alpha、MIPS等平台上的多处理器系统。
按照POSIX 1003.1c 标准编写的程序与Linuxthread 库相链接即可⽀持Linux平台上的多线程,在程序中需包含头⽂件pthread. h,在编译链接时使⽤命令:
gcc -D -REENTRANT -lpthread xxx. c
其中-REENTRANT宏使得相关库函数(如stdio.h、errno.h中函数) 是可重⼊的、线程安全的(thread-safe),-lpthread则意味着链接库⽬录下的libpthread.a或libpthread.so⽂件。使⽤Linuxthread库需要2.0以上版本的Linux内核及相应版本的C库(libc 5.2.18、libc 5.4.12、libc 6)。
2.“线程”控制
线程创建
进程被创建时,系统会为其创建⼀个主线程,⽽要在进程中创建新的线程,则可以调⽤pthread_create:
pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(start_routine)(void*), void *arg);
start_routine为新线程的⼊⼝函数,arg为传递给start_routine的参数。
每个线程都有⾃⼰的线程ID,以便在进程内区分。线程ID在pthread_create调⽤时回返给创建线程的调⽤者;⼀个线程也可以在创建后使⽤pthread_self()调⽤获取⾃⼰的线程ID:
pthread_self (void) ;
线程退出
线程的退出⽅式有三:
(1)执⾏完成后隐式退出;
(2)由线程本⾝显⽰调⽤pthread_exit 函数退出;
pthread_exit (void * retval) ;
(3)被其他线程⽤pthread_cance函数终⽌:
pthread_cance (pthread_t thread) ;
在某线程中调⽤此函数,可以终⽌由参数thread 指定的线程。
如果⼀个线程要等待另⼀个线程的终⽌,可以使⽤pthread_join函数,该函数的作⽤是调⽤pthread_join的线程将被挂起直到线程ID为参数thread的线程终⽌:
pthread_join (pthread_t thread, void** threadreturn);
3.线程通信
线程互斥
互斥意味着“排它”,即两个线程不能同时进⼊被互斥保护的代码。Linux下可以通过pthread_mutex_t 定义互斥体机制完成多线程的互斥操作,该机制的作⽤是对某个需要互斥的部分,在进⼊时先得到互斥体,如果没有得到互斥体,表明互斥部分被其它线程拥有,此时欲获取互斥体的线程阻塞,直到拥有该互斥体的线程完成互斥部分的操作为⽌。
下⾯的代码实现了对共享全局变量x ⽤互斥体mutex 进⾏保护的⽬的:
int x; // 进程中的全局变量
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL); //按缺省的属性初始化互斥体变量mutex
pthread_mutex_lock(&mutex); // 给互斥体变量加锁
… //对变量x 的操作
phtread_mutex_unlock(&mutex); // 给互斥体变量解除锁
线程同步
同步就是线程等待某个事件的发⽣。只有当等待的事件发⽣线程才继续执⾏,否则线程挂起并放弃处理器。当多个线程协作时,相互作⽤的任务必须在⼀定的条件下同步。
Linux下的编程有多种线程同步机制,最典型的是条件变量(condition variable)。pthread_cond_init⽤来创建⼀个条件变量,其函数原型为:pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr);
pthread_cond_wait和pthread_cond_timedwait⽤来等待条件变量被设置,值得注意的是这两个等待调⽤需要⼀个已经上锁的互斥体mutex,这是为了防⽌在真正进⼊等待状态之前别的线程有可能设置该条件变量⽽产⽣竞争。pthread_cond_wait的函数原型为:
pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
pthread_cond_broadcast⽤于设置条件变量,即使得事件发⽣,这样等待该事件的线程将不再阻塞:
pthread_cond_broadcast (pthread_cond_t *cond) ;
pthread_cond_signal则⽤于解除某⼀个等待线程的阻塞状态:
pthread_cond_signal (pthread_cond_t *cond) ;
pthread_cond_destroy 则⽤于释放⼀个条件变量的资源。
在头⽂件semaphore.h 中定义的信号量则完成了互斥体和条件变量的封装,按照多线程程序设计中访问控制机制,控制对资源的同步访问,提供程序设计⼈员更⽅便的调⽤接⼝。
sem_init(sem_t *sem, int pshared, unsigned int val);
这个函数初始化⼀个信号量sem 的值为val,参数pshared 是共享属性控制,表明是否在进程间共享。
sem_wait(sem_t *sem);
调⽤该函数时,若sem为⽆状态,调⽤线程阻塞,等待信号量sem值增加(post )成为有信号状态;若sem为有状态,调⽤线程顺序执⾏,但
信号量的值减⼀。
sem_post(sem_t *sem);
调⽤该函数,信号量sem的值增加,可以从⽆信号状态变为有信号状态。
4.实例
下⾯我们还是以名的⽣产者/消费者问题为例来阐述Linux线程的控制和通信。⼀组⽣产者线程与⼀组消费者线程通过缓冲区发⽣联系。⽣产者线程将⽣产的产品送⼊缓冲区,消费者线程则从中取出产品。缓冲区有N 个,是⼀个环形的缓冲池。
[cpp]
1. #include <stdio.h>
2. #include <pthread.h>
3. #define BUFFER_SIZE 16 // 缓冲区数量
4. struct prodcons
5. {
6. // 缓冲区相关数据结构
7. int buffer[BUFFER_SIZE]; /* 实际数据存放的数组*/
8. pthread_mutex_t lock; /* 互斥体lock ⽤于对缓冲区的互斥操作 */
9. int readpos, writepos; /* 读写指针*/
10. pthread_cond_t notempty; /* 缓冲区⾮空的条件变量 */
11. pthread_cond_t notfull; /* 缓冲区未满的条件变量 */
12. };
13. /* 初始化缓冲区结构 */
14. void init(struct prodcons *b)
15. {
16. pthread_mutex_init(&b->lock, NULL);
17. pthread_cond_init(&b->notempty, NULL);
18. pthread_cond_init(&b->notfull, NULL);
19. b->readpos = 0;
20. b->writepos = 0;
21. }
22. /* 将产品放⼊缓冲区,这⾥是存⼊⼀个整数*/
23. void put(struct prodcons *b, int data)
24. {
25. pthread_mutex_lock(&b->lock);
26. /* 等待缓冲区未满*/
27. if ((b->writepos + 1) % BUFFER_SIZE == b->readpos)
28. {
29. pthread_cond_wait(&b->notfull, &b->lock);
30. }
31. /* 写数据,并移动指针 */
32. b->buffer[b->writepos] = data;
33. b->writepos++;
34. if (b->writepos >= BUFFER_SIZE)
35. b->writepos = 0;
36. /* 设置缓冲区⾮空的条件变量*/
37. pthread_cond_signal(&b->notempty);
38. pthread_mutex_unlock(&b->lock);
39. }
40. /* 从缓冲区中取出整数*/
41. int get(struct prodcons *b)
42. {
43. int data;
44. pthread_mutex_lock(&b->lock);
45. /* 等待缓冲区⾮空*/
46. if (b->writepos == b->readpos)
47. {
48. pthread_cond_wait(&b->notempty, &b->lock);
49. }
50. /* 读数据,移动读指针*/
51. data = b->buffer[b->readpos];
52. b->readpos++;
53. if (b->readpos >= BUFFER_SIZE)
54. b->readpos = 0;
55. /* 设置缓冲区未满的条件变量*/
56. pthread_cond_signal(&b->notfull);
57. pthread_mutex_unlock(&b->lock);
58. return data;
59. }
60.
61. /* 测试:⽣产者线程将1 到10000 的整数送⼊缓冲区,消费者线
62. 程从缓冲区中获取整数,两者都打印信息*/
63. #define OVER ( - 1)
64. struct prodcons buffer;
65. void *producer(void *data)
66. {
67. int n;
68. for (n = 0; n < 10000; n++)
69. {
70. printf("%d --->\n", n);
71. put(&buffer, n);
72. } put(&buffer, OVER);
73. return NULL;
74. }
75.
76. void *consumer(void *data)
77. {
78. int d;
79. while (1)
80. {
81. d = get(&buffer);
82. if (d == OVER)
83. break;
84. printf("--->%d \n", d);
85. }
86. return NULL;
87. }
88.
89. int main(void)
90. {
91. pthread_t th_a, th_b;
92. void *retval;
93. init(&buffer);
94. /* 创建⽣产者和消费者线程*/
95. pthread_create(&th_a, NULL, producer, 0);
96. pthread_create(&th_b, NULL, consumer, 0);
97. /* 等待两个线程结束*/
98. pthread_join(th_a, &retval);
99. pthread_join(th_b, &retval);linux下的sleep函数
100. return 0;
101. }
5.WIN32、VxWorks、Linux线程类⽐
⽬前为⽌,笔者已经创作了《基于嵌⼊式操作系统VxWorks的多任务并发程序设计》(《软件报》2006年5~12期连载)、《深⼊浅出Win32多线程程序设计》(天极⽹技术专题)系列,我们来出这两个系列⽂章与本⽂的共通点。
看待技术问题要瞄准其本质,不管是Linux、VxWorks还是WIN32,其涉及到多线程的部分都是那些内
容,⽆⾮就是线程控制和线程通信,它们的许多函数只是名称不同,其实质含义是等价的,下⾯我们来列个三⼤操作系统共同点详细表单:
事项WIN32VxWorks Linux
线程创建CreateThread taskSpawn pthread_create
线程终⽌执⾏完成后退出;线程⾃⾝调⽤
ExitThread函数即终⽌⾃⼰;被其他线程
调⽤函数TerminateThread函数
执⾏完成后退出;由线程本⾝调
⽤exit退出;被其他线程调⽤函数
taskDelete终⽌
执⾏完成后退出;由线程本⾝调⽤
pthread_exit 退出;被其他线程调⽤函数
pthread_cance终⽌
获取线程ID GetCurrentThreadId taskIdSelf pthread_self
创建互斥CreateMutex semMCreate pthread_mutex_init
获取互斥WaitForSingleObject、
WaitForMultipleObjects
semTake pthread_mutex_lock
释放互斥ReleaseMutex semGive phtread_mutex_unlock
创建信号量CreateSemaphore semBCreate、semCCreate sem_init
等待信号量WaitForSingleObject semTake sem_wait
释放信号量ReleaseSemaphore semGive sem_post
6.⼩结
本章讲述了Linux下多线程的控制及线程间通信编程⽅法,给出了⼀个⽣产者/消费者的实例,并将Linux的多线程与WIN32、VxWorks多线程进⾏了类⽐,总结了⼀般规律。鉴于多线程编程已成为开发并发应⽤程序的主流⽅法,学好本章的意义也便不⾔⾃明。
完
[cpp]
1. #include <stdio.h>
2. #include <stdio.h>
3. #include <pthread.h>
4. void thread(void)
5. {
6. int i;
7. for(i=0;i<3;i++)
8. printf("This is a pthread.\n");
9. }
10.
11. int main(void)
12. {
13. pthread_t id;
14. int i,ret;
15. ret=pthread_create(&id,NULL,(void *) thread,NULL);
16. if(ret!=0){
17. printf ("Create pthread error!\n");
18. exit (1);
19. }
20. for(i=0;i<3;i++)
21. printf("This is the main process.\n");
22. pthread_join(id,NULL);
23. return (0);
24. }
编译:
gcc example1.c -lpthread -o example1
[cpp]
1. #include <pthread.h>
2. #include <stdio.h>
3. #include <sys/time.h>
4. #include <string.h>
5. #define MAX 10
6.
7. pthread_t thread[2];
8. pthread_mutex_t mut;
9. int number=0, i;
10.
11. void *thread1()
12. {
13. printf ("thread1 : I'm thread 1\n");
14. for (i = 0; i < MAX; i++)
15. {
16. printf("thread1 : number = %d\n",number);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论