本文秉承文章一(http://blog.csdn.net/a15261893837/article/details/77103024)所说进行书写。重点突出代码,代码与理论结合进行讲述。
在本文中将重点引用《Unix环境高级编程》第三版的代码进行讲述。将文中代码在Linux环境下运行,分析运行结果;还将引用其他博客的一些代码,以提供尽可能多的示例进行讲述。《Unix环境高级编程》网上有源代码可以下载,下载的代码有个apue.h文件是作者写的一个文件系统,系统不自带,其中包括常用头文件、出错处理函数的定义。apue.h放在apue.3e/threads中。第二版的源代码是文件apue.2e/threads。
下面给出配置apue.h方法,第一种方法是从网上找的,第二种方法是自己配置方法;
1、从网上查看配置方法,有很多这里给一个链接http://4247601.blog.51cto.com/4237601/1548616;
2、将apue.3e/include/下的apue.h复制到/usr/include下,将apue.3e/lib/error.c复制到/usr/include下;同时将代码中的#include"apue.h"改为#include<apue.h>;
本文内容概要:
一、线程创建;
二、线程终止;
Start:
一、线程创建;
线程创建函数在第一篇中已经说了。这里再一次简述一下。
线程创建:pthread_create()函数;
参数个数四个依次为线程ID、线程属性、线程函数起始地址、线程函数参数;
代码如下:
//说明代码时以vim打开的文件为根据,因vim中代码带有行号。
源代码名称:threadid.c
#include<stdlib.h>
#include <apue.h>
#include <pthread.h>
pthread_t ntid;
void
printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid,
(unsigned long)tid, (unsigned long)tid);
}
void *
thr_fn(void *arg)
{
printids("new thread: ");
return((void *)0);
}
int
main(void)
{
int err;
err = pthread_create(&ntid, NULL, thr_fn, NULL);
if (err != 0)
err_exit(err, "can't create thread");
printids("main thread:");
sleep(1);
exit(0);
}
代码运行结果如下:
图一.代码运行结果
图一为代码运行结果。屏幕左侧为代码,右侧为Shell指令及结果。
分屏采用的是tmux。下面简单说一下tmux分屏安装及常用命令。
安装:sudo apt-get install tmux
常用命令:
Ctrl+b " - split pane horizontally
Ctrl+b % - 将当前窗格垂直划分
Ctrl+b 方向键 - 在各窗格间切换
Ctrl+b,并且不要松开Ctrl,方向键 - 调整窗格大小
Ctrl+b c - (c)reate 生成一个新的窗口
Ctrl+b n - (n)ext 移动到下一个窗口
Ctrl+b p - (p)revious 移动到前一个窗口.
Ctrl+b 空格键 - 采用下一个内置布局
Ctrl+b q - 显示分隔窗口的编号
Ctrl+b o - 跳到下一个分隔窗口
Ctrl+b & - 确认后退出 tmux
tmux所有自带命令都默认需要先按Ctrl + b,然后再键入对应的命令。
实践出真知。首先在Shell中输入tmux后;然后按Ctrl + b,紧接着%;最终神奇的一幕出现了。本文中左屏幕用来显示代码,右侧屏幕输入指令;
下面说明一下屏幕右侧内容:
编译代码指令:gcc threadid.c -lpthread;
然后ls列出文件,可以看出生成了a.out;然后输入./a.out。
显示了程序结果:
代码36行sleep函数;这里主线程要休眠;否则如果主线程退出了;新的线程还没来得及执行,整个进程就结束了。读者可以将36行注释掉再运行一遍程序就会发现运行结果只有main thread那一行。
32行:如果新线程创建成功就返回0;那么err就等于0.所里33-34行就一目了然了。第一参数ntid就是新创建线程的ID;第二个参数代码新新城属性,这里为NULL代表默认属性;第三个参数thr_fn为新线程函数;第四个参数NULL代表thr_fn函数传入参数。
15行:pthread_self()函数返回线程的ID。
现在整个程序思路就理顺了。
二、线程终止;
源代码:exitstatus.cpp
#include <apue.h>
#include <pthread.h>
#include<iostream>
using namesace std;
void *
thr_fn1(void *arg)
{
printf("thread 1 returning\n");
return((void *)1);
}
void *
thr_fn2(void *arg)
{
printf("thread 2 exiting\n");
pthread_exit((void *)2);
}
int
main(void)
{
int err;
pthread_t tid1, tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, NULL);
if (err != 0)
err_exit(err, "can't create thread 1");
err = pthread_create(&tid2, NULL, thr_fn2, NULL);
if (err != 0)
err_exit(err, "can't create thread 2");
err = pthread_join(tid1, &tret);
if (err != 0)
err_exit(err, "can't join with thread 1");
printf("thread 1 exit code %ld\n", (long)tret);
err = pthread_join(tid2, &tret);
if (err != 0)
err_exit(err, "can't join with thread 2");
printf("thread 2 exit code %ld\n", (long)tret);
exit(0);
}
Linux代码运行如下:
图二. 线程终止
图二显示了程序运行结果。注意这里是.cpp文件。需要用g++编译。
Shell指令:g++ exitstatus.cpp -o ga -lpthread(顺便补充一下-lpthread是链接需要)
可以生成ga
然后执行./ga;程序结果如下:
void *则为“无类型指针”,可以指向任何数据类型,void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值。
L21-L26(这里用L代表行):这里创建了两个新线程,如果线程创建失败,输出can't create thread 1 or 2.
L14:pthread_exit(void*rval_ptr)函数,头文件pthread.h。rval_ptr是无类型指针。pthread_join()函数可以访问到该指针。
L28:pthread_join(pthread_t thread,void **rval_ptr)函数,在头文件pthread.h中.thread代表线程ID,rval_ptr代表线程返回码,就是pthread_exit函数的指针。调用线程被阻塞,直到指定线程thread调用pthread_exit()、从启动历程中返回或者被取消。L31打印出了线程1的返回码1。L28属于调用从启动历程中返回的指针,这里是(void*)1.
L32-L35:这次pthread_join()调用的是pthread_exit()函数的返回函数指针,打印为2.
pthread_join()函数调用线程一直被阻塞,直到线程退出。为了验证这个说法。本文在L13行前增加一个sleep()函数。看一看打印结果。
#include<iostream>
#include <apue.h>
#include <pthread.h>
using namespace std;
void *
thr_fn1(void *arg){
printf("thread 1 returning\n");
return((void *)1);
}
void *
thr_fn2(void *arg){
while(1){
sleep(1);
}
printf("thread 2 exiting\n");
pthread_exit((void *)2);
}
int
main(void){
int err;
pthread_t tid1, tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, NULL);
if (err != 0)
err_exit(err, "can't create thread 1");
err = pthread_create(&tid2, NULL, thr_fn2, NULL);
if (err != 0)
err_exit(err, "can't create thread 2");
err = pthread_join(tid1, &tret);
if (err != 0)
err_exit(err, "can't join with thread 1");
printf("thread 1 exit code %ld\n", (long)tret);
err = pthread_join(tid2, &tret);
if (err != 0)
err_exit(err, "can't join with thread 2");
printf("thread 2 exit code %ld\n", (long)tret);
cout<<"shh does the change"<<endl;
exit(0);
}
图三. 线程阻塞程序
L13-L14:通过增加sleep函数,可以通过右侧屏幕打印结果看出,线程2一直被阻塞,而线程1没有受到任何影响。由于阻塞导致shh does the change都没有打印出来。下面对程序做进一步修改。将线程2阻塞函数去掉。
#include<iostream>
#include <apue.h>
#include <pthread.h>
using namespace std;
void *
thr_fn1(void *arg){
printf("thread 1 returning\n");
return((void *)1);
}
void *
thr_fn2(void *arg){
while(1){
sleep(1);
}
printf("thread 2 exiting\n");
pthread_exit((void *)2);
}
int
main(void){
int err;
pthread_t tid1, tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, NULL);
if (err != 0)
err_exit(err, "can't create thread 1");
err = pthread_create(&tid2, NULL, thr_fn2, NULL);
if (err != 0)
err_exit(err, "can't create thread 2");
err = pthread_join(tid1, &tret);
if (err != 0)
err_exit(err, "can't join with thread 1");
printf("thread 1 exit code %ld\n", (long)tret);
/*err = pthread_join(tid2, &tret);
if (err != 0)
err_exit(err, "can't join with thread 2");
printf("thread 2 exit code %ld\n", (long)tret);*/
cout<<"shh does the change"<<endl;
exit(0);
}
图四.取消线程2阻塞
从图四可以看出shh does the change顺利打印出来。线程2阻塞没用影响到线程1和主线程的结果。为了保险起见最好在L40后添加一个sleep函数防止主线程过早退出。