辽宁省城乡住房和建设厅网站,公司网站域名续费一年多少钱,外贸自建站 源码,银川做网站最好的公司有哪些目录
一.POSIX线程库
二.线程创建
1.创建线程接口
2.查看线程
3.多线程的健壮性问题
4.线程函数参数传递
5.线程id和地址空间
三.线程终止
1.pthread_exit
2.pthread_cancel
四.线程等待
五.线程分离 一.POSIX线程库
站在内核的角度#xff0c;OS只有轻量级进程…目录
一.POSIX线程库
二.线程创建
1.创建线程接口
2.查看线程
3.多线程的健壮性问题
4.线程函数参数传递
5.线程id和地址空间
三.线程终止
1.pthread_exit
2.pthread_cancel
四.线程等待
五.线程分离 一.POSIX线程库
站在内核的角度OS只有轻量级进程没有线程的概念但是站在用户的角度我们只有线程没有轻量级进程的概念。因为Linux下没有真正意义上的线程而是用进程模拟的线程所以Linux不会提供直接创建线程的系统调用最多给我们提供创建轻量级进程的接口。
所以linux对下对LWP的接口进行封装对上给用户提供线程控制的接口——POSIX线程库pthread库这是任何一个linux系统都会带的库又叫原生线程库。
POSIX线程
与线程有关的函数构成了一个完整的系列绝大多数函数的名字都是以“pthread_”打头的。要使用这些函数库要通过引入头文pthread.h。链接这些线程函数库时要使用编译器命令的“-lpthread”选项。 二.线程创建
1.创建线程接口 功能创建一个新的线程。 原型 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg); 参数 thread:返回线程ID。attr:设置线程的属性attr为NULL表示使用默认属性。start_routine:是个函数地址线程启动后要执行的函数该函数返回值是void*,参数是void*。arg:传给线程启动函数的参数。 返回值 成功返回0失败返回错误码。 测试代码
#include iostream
#include cstdio
#include pthread.h
#include unistd.h
using namespace std;void *FuncRun(void *argc)
{while (1){cout I am thread,my pid: getpid() endl;sleep(1);}
}int main()
{//线程idpthread_t id;//创建线程pthread_create(id, NULL, FuncRun, NULL);while (1){cout I am main,my pid: getpid() endl;sleep(1);}return 0;
}
测试结果 说明
线程没有父子之分但是线程有主线程和新线程的区分。我们可以看到主线程pid和新线程的pid是相同的。因为他们本身就是同一个进程的一部分。新线程和主线程谁先被调度取决于调度器。 2.查看线程
查看线程使用命令:
ps -aL 3.多线程的健壮性问题 测试代码
#include iostream
#include cstdio
#include pthread.h
#include unistd.husing namespace std;void *FuncRun1(void *argc)
{int count 0;while (1){count;cout I am thread1,my pid: getpid() endl;if (count 5){int tmp count / 0;}sleep(1);}
}
void *FuncRun2(void *argc)
{while (1){cout I am thread2,my pid: getpid() endl;sleep(1);}
}int main()
{// 线程idpthread_t id1, id2;// 创建线程pthread_create(id1, NULL, FuncRun1, NULL);pthread_create(id2, NULL, FuncRun2, NULL);while (1){cout I am main,my pid: getpid() endl;sleep(1);}return 0;
}
测试结果 说明
代码其中有一个线程在第五秒的时候会出现一个除0的问题。当一个线程因为某一个错处而导致线程终止的时候整个进程也都会直接终止。因为信号是发送给进程的最终OS直接对由信号做出的处理动作也是针对进程的。 4.线程函数参数传递 我们想通过给线程函数传参让线程执行更加复杂的任务。
#include iostream
#include cstdio
#include pthread.h
#include unistd.husing namespace std;// 任务计算[1-top]的求和并将结果存储到sum中。
struct task
{task(int top, int num): _thread_name(thread to_string(num)), _top(top), _sum(0), _num(num){}string _thread_name; // 线程名字int _top; // 计算数据范围int _sum; // 结果int _num; // 线程编号
};// 线程函数
void *FuncRun(void *argc)
{task *t (task *)argc;for (int i 1; i t-_top; i){t-_sum i;}
}int main()
{// 线程idpthread_t id1, id2;// 创建线程task t1(100, 1);task t2(150, 2);pthread_create(id1, NULL, FuncRun, t1);pthread_create(id2, NULL, FuncRun, t2);// 等待线程计算完再输出结果sleep(1);cout t1._thread_name :[1- t1._top ] t1._sum endl;cout t2._thread_name :[1- t2._top ] t2._sum endl;return 0;
}
测试结果 说明
由于接口的设计上参数的类型是void* 这也就使得我们可以给线程函数传递的参数是非常丰富的。
5.线程id和地址空间
pthread_ create 函数会产生一个线程ID存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程是操作系统调度器的最小单位所以需要一个数值来唯一表示该线程。pthread_ create 函数第一个参数指向一个虚拟内存单元该内存单元的地址即为新创建线程的线程ID属于NPTL线程库的范畴。线程库的后续操作就是根据该线程ID来操作线程的。线程库NPTL提供了pthread_ self函数可以获得线程自身的ID
pthread_t pthread_self(void);
pthread_t 到底是什么类型呢取决于实现。对于Linux目前实现的NPTL实现而言pthread_t类型的线程ID本质就是一个进程地址空间上的一个地址。
我们在对线程做操作的时候根本上是使用线程库对线程进行操作那么线程库本质就是存在于linux上的动态库在我们使用线程库的时候线程库也会向普通的动态库一样加载到共享区中我们使用线程库方法就是访问自己的地址空间。
线程库中需要被管理的线程会有很多线程库也必然实现了线程的数据结构——TCB线程控制块。
每个线程都有自己的TCB和独立的上下文数据以及线程栈空间。pthread_t 就是指向他们的首地址的一个地址。 三.线程终止
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。线程可以调用pthread_ exit终止自己。一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。
1.pthread_exit 功能线程终止. 原型:void pthread_exit(void *value_ptr); 参数:value_ptr:value_ptr不要指向一个局部变量返回线程结果。 返回值无返回值跟进程一样线程结束的时候无法返回到它的调用者自身。 测试代码
#include iostream
#include cstdio
#include pthread.h
#include unistd.husing namespace std;void *FuncRun1(void *argc)
{int count 0;while (1){count;cout I am thread-1-count: count endl;sleep(1);// 三秒后线程1退出if (count 3){pthread_exit(NULL);}}
}void *FuncRun2(void *argc)
{int count 0;while (1){count;cout I am thread-2-count: count endl;sleep(1);// 三秒后线程2退出if (count 3){pthread_exit(NULL);}}
}int main()
{// 线程idpthread_t id1, id2;// 创建线程pthread_create(id1, NULL, FuncRun1, NULL);pthread_create(id2, NULL, FuncRun2, NULL);while (1){cout I am main,my pid: getpid() endl;sleep(1);}return 0;
}
测试结果 说明
线程在退出后主线程并没有受到影响进程也有没受到影响。需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
2.pthread_cancel 功能取消一个执行中的线程 原型int pthread_cancel(pthread_t thread); 参数thread:线程ID 返回值成功返回0失败返回错误码 测试代码 #include iostream
#include cstdio
#include pthread.h
#include unistd.husing namespace std;void *FuncRun1(void *argc)
{int count 0;while (1){count;cout I am thread-1-count: count endl;sleep(1);}
}
void *FuncRun2(void *argc)
{int count 0;while (1){count;cout I am thread-2-count: count endl;sleep(1);}
}
int main()
{// 线程idpthread_t id1, id2;// 创建线程pthread_create(id1, NULL, FuncRun1, NULL);pthread_create(id2, NULL, FuncRun2, NULL);int count 0;while (1){count;sleep(1);if (count 3){// 三秒后终止线程pthread_cancel(id1);pthread_cancel(id2);}cout I am main endl;}return 0;
}
测试结果 四.线程等待
为什么需要线程等待
已经退出的线程其空间没有被释放仍然在进程的地址空间内。创建新的线程不会复用刚才退出线程的地址空间。 功能等待线程结束。 原型int pthread_join(pthread_t thread, void **value_ptr); 参数thread:线程ID。 value_ptr它指向一个指针后者指向线程的返回值。 返回值成功返回0失败返回错误码。 调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的总结如下:
如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_ CANCELED。如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。 测试代码
void *FuncRun1(void *argc)
{int *top (int *)argc;int *sum new int;for (int i 1; i *top; i){*sum i;}// 线程退出pthread_exit(sum);
}void *FuncRun2(void *argc)
{int *top (int *)argc;int *sum new int;for (int i 1; i *top; i){*sum i;}// 线程退出return sum;
}void *FuncRun3(void *argc)
{int *top (int *)argc;int *sum new int;for (int i 1; i *top; i){*sum i;sleep(1);}free(sum);// 线程退出
}int main()
{int top1 100;int top2 150;int top3 200;pthread_t id1;pthread_t id2;pthread_t id3;pthread_create(id1, NULL, FuncRun1, top1);pthread_create(id2, NULL, FuncRun2, top2);pthread_create(id3, NULL, FuncRun3, top3);pthread_cancel(id3);// 接受线程返回数据void *ret_ptr1;void *ret_ptr2;void *ret_ptr3;// 等待线程pthread_join(id1, ret_ptr1);pthread_join(id2, ret_ptr2);pthread_join(id3, ret_ptr3);cout ret1: *((int *)ret_ptr1) endl;free(ret_ptr1);cout ret2: *((int *)ret_ptr2) endl;free(ret_ptr2);if (ret_ptr3 PTHREAD_CANCELED)cout ret3:PTHREAD_CANCELED endl;return 0;
}
测试结果 五.线程分离 默认情况下新创建的线程是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄漏。如果不关心线程的返回值join是一种负担这个时候我们可以告诉系统当线程退出时自动释放线程资源。
int pthread_detach(pthread_t thread);
可以是线程组内其他线程对目标线程进行分离也可以是线程自己分离:
pthread_detach(pthread_self());
joinable和分离是冲突的一个线程不能既是joinable又是分离的。
测试代码 #include iostream
#include cstdio
#include cstring
#include cerrno
#include pthread.h
#include unistd.husing namespace std;
void *FuncRun(void *argc)
{// 线程分离pthread_detach(pthread_self());int count 0;while (1){count;cout I am thread-count: count endl;sleep(1);}
}int main()
{pthread_t tid;int n pthread_create(tid, NULL, FuncRun, NULL);if (n ! 0){cerr pthread_create: strerror(errno) endl;}sleep(2);// 线程已经分离再去线程等待pthread_join会立即报错。if (pthread_join(tid, NULL) 0){printf(pthread wait success\n);}else{printf(pthread wait failed\n);}return 0;
}测试结果