linux 多线程程序设计
1.引入线程的原因:
a. 和进程相比,线程是一种非常“节俭”的多任务操作方式。
在linux系统中,启动一个新的进程必须给它分配一个独立的地址空间,建立众多的数据表来维
护它的代码段、堆栈段和数据段,这是一种“昂贵”的多任务工作方式。
运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需的时间
也远小于进程间切换所需要的时间。
据统计,一个进程的开销大约是一个线程开销的30倍左右。
b. 线程间通信机制方便。
不同的进程,它们具有独立的数据空间,要进行数据传递只能通过进程间通信方式进行,这种方
式即费时,也很不方便,
由于在同一进程下的线程之间共享数据段空间,所以一个线程的数据可以直接给其他线程使用,
这不仅快捷,而且方便。
2. 多线程优点:
a. 使多CPU系统更加有效。
操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
b. 改善程序结构。
一个即长又复杂的进程,可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程
序会利于理解和修改。
3. linux系统下的多线程遵循 POSIX 线程接口,称为pthread.
编写linux下的多线程程序,需要使用头文件 pthread.h, 链接时需要使用库 libpthread.a
如: gcc thread_create -lpthread -o thread_create
4. 创建线程:
#include <pthread.h>
int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, void *
(*start_rtn)(void), void *arg);
tidp --- 线程id
attr --- 线程属性(通常为空)
start_rtn --- 线程要执行的函数,这函数执行完了,线程也就结束了。
arg --- start_rtn 的参数。
5. 编译
因为 pthread 的库,不是linux系统的库,所以在编译时要加上 -lpthread
如: #gcc filename -lpthread
例: pthread_create.c
#include <stdio.h>
#include <pthread.h>
void *myThread1(void)
{
int i;
for(i = 0; i < 100; i++)
{
printf("This is the 1st pthread, created by zieckey.\n");
sleep(1);
}
}
void *myThread2(void)
{
int i;
for(i = 0; i < 100; i++)
{
printf("This is the 2nd pthread, created by zieckey.\n");
sleep(1);
}
}
int main()
{
int i = 0, ret = 0;
pthread_t id1, id2;
//创建线程1
ret = pthread_create(&id1, NULL, (void*)myThread1, NULL);
if (ret){
printf("Create pthread error!\n");
return 1;
}
//创建线程2
ret = pthread_create(&id1, NULL, (void*)myThread2, NULL);
if (ret){
printf("Create pthread error!\n");
return 1;
}
pthread_join(id1, NULL);
pthread_join(id2, NULL);
}
pthread_int.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
{
int *num; //num是指针
num = (int *) arg; //指针赋值
printf("create parameter is %d\n", *num); //*num 是指针所指内容,即指针地址中的值
return (void *)0;
}
int main(int argc, char *argv[])
{
pthread_t tidp;
int error;
int test = 4;
int *attr = &test; //变量地址&test 赋给 指针*attr
error = pthread_create(&tidp, NULL, create, (void *)attr); //attr指针 作为参数 传给线程
if(error)
{
printf("pthread_create is not created ...\n");
return -1;
}
sleep(1);
printf("pthread is created!\n");
return 0;
}
pthread_share.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int a = 1; //这段代码说明了 线程之间 共享 静态变量,堆, 数据段
void *create(void *arg)
{
printf("new thread ...\n");
printf("a = %d \n", a);
a++; //在新线程中++
return (void *)0;
}
int main(int argc, char *argv[])
{
pthread_t tidp;
int error;
int a = 5; //新增了 局部变量a之后,原进程中会优先使用栈中的局部变量,所以main 1: a = 5;
//而新线程中,没有局部变量a,只能使用静态变量a = 1;
//新线程结束后,局部变量a没变,还是5,所以main 2: a = 5;
//int a = 5; //当这句没有时,两个线程都使用全局变量 a = 1;
printf("in main 1: a = %d\n", a);
error = pthread_create(&tidp, NULL, create, NULL);
if(error)
{
printf("new pthread is not created ...\n");
return -1;
}
sleep(3);
printf("in main 2: a = %d\n", a); //这里打印2,说明变量经过新线程+1了。
printf("new thread is created!\n");
return 0;
}
6. 终止线程
如果进程中的 任何一个线程中调用了 exit 或 _exit, 那么整个进程都会终止。
线程正常退出方式有:
a. 线程从启动例程中返回;
b. 线程可以被另一个进程终止;
c. 线程自己调用 pthread_exit 函数。
#include <pthread.h>
void pthread_exit(void *rval_ptr)
功能:终止调用线程
rval_ptr --- 线程推出返回值的指针;
例thread_exit.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
{
printf("new thread is created ...\n");
return (void *)8;
}
int main(int argc, char *argv[])
{
pthread_t tid;
int error;
void *temp;
error = pthread_create(&tid, NULL, create, NULL);
printf("main thread!\n");
if(error){
printf("thread is not created ... \n");
return -1;
}
error = pthread_join(tid, &temp);
if(error){
printf("thread is not exit ... \n");
return -2;
}
printf("thread is exit code %d \n", (int)temp);
return 0;
}
7. 线程等待:
#include <pthread.h>
int pthread_join(pthread_t tid, void **rval_ptr)
功能:阻塞调用线程,直到指定线程终止
tid --- 等待退出的线程id;
rval_ptr --- 线程退出的返回值的指针。
例 thread_join.c
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
void *thread(void *str)
{
int i;
for(i = 0; i < 3; ++i){
sleep(2);
printf("This is in the thread : %d\n", i);
}
return NULL;
}
int main()
{
pthread_t pth;
int i;
int ret = pthread_create(&pth, NULL, thread, (void *)(i));
// pthread_join(pth, NULL); //起阻塞作用,阻塞该线程,直到pth线程退出为止,才开始运行下面代码
printf("123\n");
for(i = 0; i < 3; i++){
sleep(2);
printf("This is in the main: %d\n", i);
}
return 0; //进程先退出时,里面的线程自动退出。
}
8. 线程标识
#include <pthread.h>
pthread_t pthread_self(void)
功能:获取调用线程的 thread id; 类似于进程的getpid()
例 thread_id.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
{
printf("New thread ...\n");
printf("This thread id is %u\n", (unsigned int)pthread_self());
printf("This process pid is %d\n", getpid());
return (void *)0;
}
int main(int argc, char *argv[])
{
pthread_t tid;
int error;
printf("Main thread is starting\n");
error = pthread_create(&tid, NULL, create, NULL);
if (error) {
printf("thread is not created ...\n");
return -1;
}
printf("The main process pid is %d\n", getpid());
sleep(1);
return 0;
}
9. 清除线程
线程终止有两种情况:
a. 正常终止:线程主动调用 pthread_exit 或者 从线程函数中 return, 这是可预见的线程退出方式
b. 非正常退出:线程在其他线程干预下,或者自身运行出错(比如非法地址访问)而退出,这种退出方式是不可预见的。
无论是哪种终止方式,都会存在资源释放的问题,都需要保证线程终止时,能够顺利的释放掉自己所占的资源
从 pthread_cleanup_push 的调用点,到pthread_cleanup_pop 之间的程序段 中的终止动作(包括pthread_exit()和异常终止,不包括return),都将执行 pthread_cleanup_push() 所制定的清理函数。
#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void *), void *arg)
功能:将清楚函数压入清除栈
rtn --- 清除函数;
arg --- 清除函数参数。
void pthread_cleanup_pop(int execute)
功能: 将清除函数弹出清除栈
execute --- 执行到 pthread_cleanup_pop() 时,用来判断是否执行清除函数,非0:执行(就算有return都会执行), 0:不执行。
例 thread_clean.c
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
void *clean(void *arg)
{
printf("cleanup : %s\n", (char *)arg);
return (void *)0;
}
void *thr_fn1(void *arg)
{
printf("thread 1 start \n");
pthread_cleanup_push((void *)clean, "thread 1 first handler"); //线程中没有return时,会执行指定清除函数clean。
pthread_cleanup_push((void *)clean, "thread 1 second handler");
printf("thread 1 push complete\n");
if (arg) {
return (void *)1; //如有有return ,就不会执行制定清除函数clean.
}
pthread_cleanup_pop(0); //参数为1时, 会开始执行清除函数
pthread_cleanup_pop(0);
return (void *)1; //如有有return ,就不会执行制定清除函数clean.
}
void *thr_fn2(void *arg)
{
printf("thread 2 start \n");
pthread_cleanup_push((void *)clean, "thread 2 first handler"); //线程中没有return时,包括pthread_exit()在内,都会执行指定函数。
pthread_cleanup_push((void *)clean, "thread 2 second handler");
printf("thread 2 push complete\n");
if (arg) {
pthread_exit((void *)2);
}
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
pthread_exit((void *)2);
}
int main(void)
{
int err;
pthread_t tid1,tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);
if (err != 0){
printf("error thread 1...\n");
return -1;
}
err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);
if (err != 0){
printf("error thread 2...\n");
return -1;
}
err = pthread_join(tid1, &tret);
if (err != 0){
printf("error thread join...\n");
return -1;
}
printf("thread 1 exit code %d \n", (int) tret);
err = pthread_join(tid2, &tret);
if (err != 0){
printf("error thread join...\n");
return -1;
}