《线程》,对于一个程序员,如果不懂得线程,那么我觉得他对基础的高级程序还是没有入门的。
线程里有几个概念,包括创建,终止,同步,调度,进程交互等概念。
线程并不维持线程表,而是由一个进程维护。
一个进程里的多个线程共享进程的地址空间。
共享以下:
Process instructions
大多数数据
打开的描述符
信号和信号句柄
当前的工作目录
用户和组ID.
但是每个线程唯一拥有的是
线程id,
寄存器集合和栈指针
局部变量的栈内存和返回地址
信号。优先级
返回值。
下面主要贴一下那几个主要函数吧。
pthreat_t 线程变量
1创建缺省线程
intpthread_create(pthread_t *tid,cons、劈劈啪啪劈劈啪啪破婆婆tpthread_attr_t *tattr,
void*(*start_routine)(void *), void *arg);
参数:
当pthread_create() 成功时,所创建线程的ID 被存储在由tid 指向的位置中。第二个参数用于设置线程属性,如果不需要特殊的属性,可以简单的设置该参数为NULL,最后两个参数告诉线程将要启动执行的函数和传递给该函数的参数。
返回值:
调用成功完成后返回0,其他的值都表示出现错误。
如果检测到以下任一情况,pthread_create() 将失败并返回相应的值。
EAGAIN 超出了系统限制,如创建的线程太多。
EPERM 调用者没有适当的权限设置所需的参数或安排调度策略
EINVAL 描述: tattr 的值无效。(设置的属性有问题)
默认属性:绑定,非分离,继承创建者线程中定义的调度策略。
2终止线程
voidpthread_exit(void *status);
本函数可用来终止调用线程。将释放调用线程所有特定数据绑定。如果调用线程尚未分离,则线程ID 和status 指定的退出状态将保持不变,直到应用程序调用pthread_join()以等待该线程。否则,将忽略status。线程ID 可以立即回收。
有一个重要的特殊情况,即当初始线程(即调用main() 的线程)从main() 调用返回时或调用exit() 时,整个进程及其所有的线程将终止。因此,一定要确保初始线程不会从main()过早地返回,在其它线程调用exit()也会终止整个进程。
请注意,如果主线程仅仅调用了pthread_exit,则仅主线程本身终止。进程及进程内的其他线程将继续存在。所有线程都已终止时,进程也将终止。
3等待线程终止
intpthread_join(thread_t tid,void **status);
参数:
参数tid指定要等待的线程ID,指定的线程必须位于当前的进程中,而且不得是分离线程。
当参数status 不是NULL 时,status 指向某个位置,在pthread_join()成功返回时,将该位置设置为已终止线程的退出状态。
返回值:
调用成功完成后,pthread_join() 将返回零。其他任何返回值都表示出现了错误。如果检测到以下任一情况,pthread_join() 将失败并返回相应的值。
ESRCH 描述: 没有找到与给定的线程ID 相对应的线程。
EDEADLK 描述: 将出现死锁,如一个线程等待其本身,或者线程A和线程B 互相等待。
EINVAL 描述: 与给定的线程ID 相对应的线程是分离线程。
说明:
如果多个线程等待同一个线程终止,则所有等待线程将一直等到目标线程终止。然后,一个等待线程成功返回。其余的等待线程将失败并返回ESRCH 错误。
pthread_join() 仅适用于非分离的目标线程。如果没有必要等待特定线程终止之后才进行其他处理,则应当将该线程分离。
pthread_exit与pthread_join进一步说明:(一下主线程是main线程)
1.线程自己运行结束,或者调用pthread_exit()结束,线程都会释放自己独有的空间资源。
2.如果线程是非分离的,线程会保留线程id号,直到其他线程通过"joining"这个线程确认其已死亡。join 的结果是joining 线程得到已终止线程的退出状态,已终止的线程将消失。
3.如果线程是分离的,不需要使用pthread_exit(),线程自己运行结束,就会释放所有资源(包括线程id号)。
4.子线程最终一定要使用pthread_join()或者设置为分离状态来结束线程,否则线程的资源不会被完全释放。(使用取消线程功能也不能完全释放)
5.主线程运行pthread_exit(),会结束主线程,但不会结束子线程。
6.主线程结束,则整个程序结束,所以主线程最好要使用join等待子线程运行结束。使用join一个线程可以等待多个线程结束。
7.使用join的线程将会阻塞,直到被join的线程结束,join函数返回,但是它对被join的线程运行没有影响。
8.如果子线程使用exit()则可以结束整个进程
4.分离线程
intpthread_detach(thread_t tid);
函数用于指示应用程序在线程tid 终止时回收其存储空间。如果tid 尚未终
止,pthread_detach() 不会终止该线程。
返回值:
pthread_detach()在调用成功完成之后返回零。其他任何返回值都表示出现了错误。如果检测到以下任一情况,pthread_detach() 将失败并返回相应的值。
EINVAL 描述: tid 是分离线程。
ESRCH 描述: tid 不是当前进程中有效的未分离的线程。
注意:为了避免线程的资源在线程结束时不能得到正确释放,从而避免产生潜在的内存泄漏问题,在对待线程结束时,要确保该线程处于detached 状态,否着就需要调用 pthread_join() 函数来对其进行资源回收。(如果不进行以上操作,线程id号不会被回收)。但是如果主线程结束,整个程序就结束了,所以最好在主线程使用,join等待其它线程结束。
5获取线程标识符
pthread_tpthread_self(void);
返回调用线程的标识符,(是一个无符号整形数)
6比较线程ID
intpthread_equal(pthread_t tid1,pthread_t tid2);
如果tid1 和tid2 相等,pthread_equal() 将返回非零值,否则将返回零。如果tid1 或tid2 是无效的线程标识号,则结果无法预测。
77一次性初始化
int pthread_once(pthread_once_t*once_control, void (*init_routine) (void))
本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。
8简单例子
- #include<stdio.h>
- #include<stdlib.h>
- #include<pthread.h>
- void* print_message_function(void*);
- int main()
- {
- pthread_t thread1,thread2;
- char* mess1="thread1:this is first thread1!!";
- char* mess2="thread2:this is second thread2!!";
- int pth1,pth2;
- pth1=pthread_create(&thread1,NULL,print_message_function,(void*)mess1);
- pth2=pthread_create(&thread2,NULL,print_message_function,(void*)mess2);
- pthread_join(thread1,NULL);
- pthread_join(thread2,NULL);
- printf("-----------------------------\n");
- printf("Thread 1 returns: %d\n",pth1);
- printf("-----------------------------\n");
- printf("Thread 2 returns: %d\n",pth2);
- exit(0);
- }
- void* print_message_function(void* v_message)
- {
- char* message;
- message=(char*)v_message;
- printf("haha~%s\n",message);
- }
Compile:
- C compiler: cc -lpthread pthread1.c
or - C++ compiler: g++ -lpthread pthread1.c
Run: ./a.out
Results:
-
Thread 1 Thread 2 Thread 1 returns: 0 Thread 2 returns: 0
线程同步
初始化互斥锁
1.动态初始化:
intpthread_mutex_init(pthread_mutex_t *mp,
constpthread_mutexattr_t *mattr);
第一个参数指向互斥锁,第二个参数指向互斥锁的属性对象,将mattr 设置为NULL的效果与传递缺省互斥锁属性对象的地址相同,但是没有内存开销。
2.静态初始化:
使用PTHREAD_MUTEX_INITIALIZER 宏可以将以静态方式定义的互斥锁初始化为其缺省属性。
返回值
在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EBUSY 描述: 该实现已检测到系统尝试重新初始化mp 所引用的对象,即以前进行过初始化但尚未销毁的互斥锁。
EINVAL 描述: mattr 属性值无效。互斥锁尚未修改。
EFAULT 描述: mp 所指向的互斥锁的地址无效。
注意:如果针对以前初始化的互斥锁调用 pthread_mutex_init(),则该互斥锁还会被重新初始化。
18.销毁互斥锁
intpthread_mutex_destroy(pthread_mutex_t *mp);
此函数没意义,执行之后对锁没有影响,可以不用。
初始化互斥锁
1.动态初始化:
intpthread_mutex_init(pthread_mutex_t *mp,
constpthread_mutexattr_t *mattr);
第一个参数指向互斥锁,第二个参数指向互斥锁的属性对象,将mattr 设置为NULL的效果与传递缺省互斥锁属性对象的地址相同,但是没有内存开销。
2.静态初始化:
使用PTHREAD_MUTEX_INITIALIZER 宏可以将以静态方式定义的互斥锁初始化为其缺省属性。
返回值
在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EBUSY 描述: 该实现已检测到系统尝试重新初始化mp 所引用的对象,即以前进行过初始化但尚未销毁的互斥锁。
EINVAL 描述: mattr 属性值无效。互斥锁尚未修改。
EFAULT 描述: mp 所指向的互斥锁的地址无效。
注意:如果针对以前初始化的互斥锁调用 pthread_mutex_init(),则该互斥锁还会被重新初始化。
19.锁定互斥锁
intpthread_mutex_lock(pthread_mutex_t *mutex);
当pthread_mutex_lock() 返回时,该互斥锁已被锁定。调用线程是该互斥锁的属主。如果该互斥锁已被另一个线程锁定和拥有,则调用线程将阻塞,直到该互斥锁变为可用为止。
返回值:
在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EAGAIN 描述: 由于已超出了互斥锁递归锁定的最大次数,因此无法获取该互斥锁。
EDEADLK 描述: 当前线程已经拥有互斥锁
20.解除锁定互斥锁
intpthread_mutex_unlock(pthread_mutex_t *mutex);
可释放mutex 引用的互斥锁对象。互斥锁的释放方式取决于互斥锁的类型属性。如果调用pthread_mutex_unlock() 时有多个线程被mutex对象阻塞,则互斥锁变为可用时调度策略可确定获取该互斥锁的线程。对PTHREAD_MUTEX_ERRORCHECK_NP类型的互斥锁,当计数达到零并且调用线程不再对该互斥锁进行任何锁定时,该互斥锁将变为可用。
返回值
在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
EPERM 描述: 当前线程不拥有互斥锁。
- #include<stdio.h>
- #include<stdlib.h>
- #include<pthread.h>
- pthread_mutex_t mutex1= PTHREAD_MUTEX_INITIALIZER;
- int counter=0;
- void *functionC();
- int main()
- {
- int pth1,pth2;
- pthread_t pthread1,pthread2;
- pth1=pthread_create(&pthread1,NULL,functionC,NULL);
- pth2=pthread_create(&pthread2,NULL,functionC,NULL);
- pthread_join(pthread1,NULL);
- pthread_join(pthread2,NULL);
- return 0;
- }
- void* functionC()
- {
- pthread_mutex_lock(&mutex1);
- counter++;
- printf("Counter value: %d\n",counter);
- pthread_mutex_unlock(&mutex1);
- }
29.初始化条件变量
动态方式:intpthread_cond_init(pthread_cond_t *cv,
constpthread_condattr_t *cattr);
静态方式:使用PTHREAD_COND_INITIALIZER 宏可以将以静态方式定义的条件变量初始化为其缺省属性。PTHREAD_COND_INITIALIZER 宏与动态分配具有null属性的pthread_cond_init()等效,但是不进行错误检查。
pthread_cond_init()在成功完成之后会返回零。
30.基于条件变量阻塞
intpthread_cond_wait(pthread_cond_t *cv,pthread_mutex_t*mutex);
阻塞的线程可以通过pthread_cond_signal() 或pthread_cond_broadcast()唤醒,也可以在信号传送将其中断时唤醒。
不能通过pthread_cond_wait() 的返回值来推断与条件变量相关联的条件的值的任何变化。必须重新评估此类条件。
pthread_cond_wait()例程每次返回结果时调用线程都会锁定并且拥有互斥锁,即使返回错误时也是如此。
该条件获得信号之前,该函数一直被阻塞。该函数会在被阻塞之前以原子方式释放相关的互斥锁,并在返回之前以原子方式再次获取该互斥锁。
通常,对条件表达式的评估是在互斥锁的保护下进行的。如果条件表达式为假,线程会基于条件变量阻塞。然后,当该线程更改条件值时,另一个线程会针对条件变量发出信号。这种变化会导致所有等待该条件的线程解除阻塞并尝试再次获取互斥锁。必须重新测试导致等待的条件,然后才能从pthread_cond_wait() 处继续执行。唤醒的线程重新获取互斥锁并从pthread_cond_wait()返回之前,条件可能会发生变化。等待线程可能并未真正唤醒。建议使用的测试方法是,将条件检查编写为调用pthread_cond_wait() 的while() 循环。
31.解除阻塞一个线程
intpthread_cond_signal(pthread_cond_t *cv);
应在互斥锁的保护下修改相关条件,该互斥锁用于获得信号的条件变量中。否则,可能在条件变量的测试和pthread_cond_wait() 阻塞之间修改该变量,这会导致无限期等待。
默认设置下还是唤醒第一次被加入的线程,如果没有任何线程基于条件变量阻塞,则调用pthread_cond_signal() 不起作用。
31.解除阻塞所有线程
intpthread_cond_broadcast(pthread_cond_t *cv);
例子:
- #include <stdio.h>
- #include <stdlib.h>
- #include <pthread.h>
- pthread_mutex_t cond_mutex=PTHREAD_MUTEX_INITIALIZER;
- pthread_cond_t cond_var=PTHREAD_COND_INITIALIZER;
- void* function_cond1();
- void* function_cond2();
- int count=0;
- #define COUNT_DONE 10
- #define COUNT_HALT1 3
- #define COUNT_HALT2 6
- int main()
- {
- pthread_t thread1,thread2;
- pthread_create(&thread1,NULL,function_cond1,NULL);
- pthread_create(&thread2,NULL,function_cond2,NULL);
- pthread_join(thread1,NULL);
- pthread_join(thread2,NULL);
- printf("Final count: %d\n",count);
- return 0;
- }
- void* function_cond1()
- {
- for(;;)
- {
- pthread_mutex_lock(&cond_mutex);
- pthread_cond_wait(&cond_var,&cond_mutex);
- count++;
- printf("Counter value functionCount1: %d\n",count);
- pthread_mutex_unlock(&cond_mutex);
- if(count>=COUNT_DONE) return ;
- }
- }
- void* function_cond2()
- {
- for(;;)
- {
- pthread_mutex_lock(&cond_mutex);
- if(count<COUNT_HALT1||count>COUNT_HALT2)
- {
- // Condition of if statement has been met.
- // Signal to free waiting thread by freeing the mutex.
- // Note: functionCount1() is now permitted to modify "count".
- pthread_cond_signal(&cond_var);
- }
- else
- {
- count++;
- printf("Counter value functionCount2: %d\n",count);
- }
- pthread_mutex_unlock(&cond_mutex);
- if(count>=COUNT_DONE) return ;
- }
- }
输出结果:
Counter value functionCount1: 1
Counter value functionCount1: 2
Counter value functionCount1: 3
Counter value functionCount2: 4
Counter value functionCount2: 5
Counter value functionCount2: 6
Counter value functionCount2: 7
Counter value functionCount1: 8
Counter value functionCount1: 9
Counter value functionCount1: 10
Final count: 10