一、线程的概念
线程:进程中的一个实体,是CPU调度和分派的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、一组寄存器和栈),但它可以与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。线程在运行中呈现间断性。(以上来自《计算机四级教程——操作系统原理》)
谈到线程,就有必要说说进程的定义:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。(以上来自《计算机四级教程——操作系统原理》)
进程的定义有点绕口,我们看看进程由什么组成:程序、数据和进程控制块。其中程序,对应进程定义中“具有一定独立功能的程序”,但是进程除了程序本身,还需要有数据(可以理解为资源),以及,进程控制块。数据和进程控制块是程序运行时必不可少的资源,程序依赖这些资源进行相应的活动,就是我们说的“进程”了。
进程的两个基本属性:
进程是一个可拥有资源的独立单位;
进程是一个可以独立调度和分派的基本单位。
线程建立之初,就是为了将进程的上述两个属性分开,线程构成了“CPU调度和分派的基本单位”,这样一个进程中可以有很多线程,操作系统对线程进行调度和分派,可以更好地实现进程的并打执行;同时同一个进程下的线程可以共享该进程的全部资源,可以满足同一个进程下不同线程对进程资源的访问。线程的出现,巧妙地将进程的两个属性分开,使得进程可以更好地处理并行执行的需求。
线程就是一个正在运行的函数。posix线程是一套标准,而不是一套实现。还有别的标准如:openmp线程。
线程标识:pthread_t (不知道具体的内容,各家实现不同,linux下是int)
px axm命令 看到进程和线程--代表进程下的线程。 px ax -L查看轻量级线程。
int pthread_equal(pthread_t t1, pthread_t t2); 比较两个线程的ID号。相同返回非0,不同返回0.
pthread_t pthread_self(void); 获取当前线程的线程ID
二、线程的创建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
线程的调度取决与调度器的策略。
create1.c
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
void* myfunc(void *p)
{
puts("Thread is run!");
printf("thread %ld \n",pthread_self());
return NULL;
}
int main()
{
puts("Begin!");
pthread_t tid;
int ret;
ret = pthread_create(&tid,NULL,myfunc ,NULL);
if(ret)
{
fprintf(stderr,"%s \n",strerror(ret));
exit(1);
}
printf("main %ld \n",pthread_self());
puts("End!");
}
线程的终止
1.线程从启动例程返回,返回值就是线程的退出码。
2. 线程可以被同一进程中的其他线程取消。
3.线程调用pthread_exit()函数。 void pthread_exit(void *retval);
int pthread_join(pthread_t thread, void **retval); 相当于进程的wait,用于收尸。
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
void* myfunc(void *p)
{
puts("Thread is run!");
pthread_exit(NULL);//线程专用清理函数。
// return NULL;
}
int main()
{
puts("Begin!");
pthread_t tid;
int ret;
ret = pthread_create(&tid,NULL,myfunc ,NULL);
if(ret)
{
fprintf(stderr,"%s \n",strerror(ret));
exit(1);
}
pthread_join(tid,NULL); //收尸
puts("End!");
}
栈的清理
pthread_cleanup_push(); //相当于atexit
pthread_cleanup_pop(); //相当于可以主动取数据。
void pthread_cleanup_push(void (*routine)(void *), 是宏的实现,gcc -E查看预处理
void *arg);
void pthread_cleanup_pop(int execute); //选择是否调用。必须成对出现,用宏实现
cleanup.c
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
void cleanup_fun(void*p)
{
puts(p);
}
void* myfunc(void *p)
{
puts("Thread is run!");
pthread_cleanup_push(cleanup_fun,"cleanup:1");
pthread_cleanup_push(cleanup_fun,"cleanup:2");
pthread_cleanup_push(cleanup_fun,"cleanup:3");
puts("push over!");
pthread_exit(NULL);//线程专用清理函数。
// return NULL;
pthread_cleanup_pop(1) //线程退出后,全部都会调用;
pthread_cleanup_pop(0);
pthread_cleanup_pop(1);
}
int main()
{
puts("Begin!");
pthread_t tid;
int ret;
ret = pthread_create(&tid,NULL,myfunc ,NULL);
if(ret)
{
fprintf(stderr,"%s \n",strerror(ret));
exit(1);
}
pthread_join(tid,NULL); //收尸
puts("End!");
}
线程的取消选项
正在运行的线程要是想要收尸收回来,那么就需要先进行取消(pthread_cancel)在进行收尸(pthread_join)。
线程取消:int pthread_cancel(pthread_t thread);
取消有两种状态:允许和不允许。
不允许取消:继续执行代码,不受任何影响。
允许取消又分为:异步cancel,和推迟cancel(默认)->推迟到cancel点在响应。
cancal点:Posix定义的cancel点,都是可能引发堵塞的系统调用。
pthread_setcancelstate:可以设置取消状态。
pthread_setcanceltype:可以设置取消方式。
pthread_testcancel:函数什么都不做,就是取消点。
线程分离: int pthread_detach(pthread_t thread);
动态模块单次初始化函数:int pthread_once(pthread_once_t *once_control,
void (*init_routine)(void));
pthread_once_t once_control = PTHREAD_ONCE_INIT;
实例1
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define LEFT 30000000
#define RIGHT 30000200
#define THRNUM (RIGHT-LEFT+1)
void* thr_prime(void*p);
int main()
{
pthread_t tid[THRNUM];
int i,j,mark;
int err;
for(i =LEFT;i<=RIGHT;i++)
{
err= pthread_create(tid+(i-LEFT),NULL,thr_prime,&i);
if(err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
}
}
for(i=LEFT;i<=RIGHT;i++)
{
pthread_join(tid[i-LEFT],NULL);
}
return 0;
}
void* thr_prime(void*p)
{
int i,j,mark;
i = *(int*)p;
mark = 1;
for(j=2;j<i/2;j++)
{
if(i%j ==0)
{
mark = 0;
break;
}
}
if(mark)
printf("%d is a primer \n",i);
pthread_exit(NULL);
return NULL;
}
以上代码运行会出现竞争的现象。因为传递参数用的是地址传参,数据需要取*才能拿到,不能保证上一个线程是否进行了该操作,最简单的是使用值传递。
primer0.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define LEFT 30000000
#define RIGHT 30000200
#define THRNUM (RIGHT-LEFT+1)
void* thr_prime(void*p);
int main()
{
pthread_t tid[THRNUM];
int i,j,mark;
int err;
for(i =LEFT;i<=RIGHT;i++)
{
err= pthread_create(tid+(i-LEFT),NULL,thr_prime,(void *)i);//值传递,因为参数为地址,故强转成地址
// err= pthread_