线程
1、线程的概念
一个正在运行的函数
线程间通信会比进程间通信简单点。
posix线程是一套标准,而不是实现。
openmp线程
线程标识:pthread_t (理解为一个整型数)
ps下面的 - - 是线程。进程来消耗当前的进程号的。进程就是容器用来承载线程。
pthread_equal();//比较线程标识
pthread_self();//返回当前线程标识
2、线程的创建
pthread_create();
线程的调度取决于调度器调度。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
static void *func(void*p)
{
puts("thread is working!\n");
return NULL;
}
int main()
{
pthread_t tid;
int err;
puts("Begin!\n");
err = pthread_create(&tid,NULL,func,NULL);
if (err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
puts("End!\n");
exit(0);
}
线程的终止
线程的终止有三种方式:
1)线程从启动例程中返回,返回值为线程的退出码
2)线程可以被同一进程中的其他线程取消
3)线程调用pthread_exit()函数
pthread_join() --> wait(); //收尸
线程的栈清理
//挂钩子函数
pthread_cleanup_push();
//取钩子函数
pthread_cleanup_pop();
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
static void cleanup_func(void *p)
{
puts(p);
}
static void *func(void*p)
{
puts("thread is working!\n");
pthread_cleanup_push(cleanup_func,"cleanup:1");
pthread_cleanup_push(cleanup_func,"cleanup:2");
pthread_cleanup_push(cleanup_func,"cleanup:3");
puts("push over!\n");
//1表示调用,0表示不调用,成对存在
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_exit(NULL);
//return NULL;
}
int main()
{
pthread_t tid;
int err;
puts("Begin!\n");
err = pthread_create(&tid,NULL,func,NULL);
if (err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
pthread_join(tid,NULL);
puts("End!\n");
exit(0);
}
多线程实现求质数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#define LEFT 30000000
#define RIGHT 30000200
#define THRNUM (RIGHT-LEFT+1)
static void *thr_prime(void *p);
int main()
{
int err,i,j,mark;
pthread_t tid[THRNUM];
for ( i = LEFT; i <= RIGHT; i++)
{
err = pthread_create(tid+(i-LEFT),NULL,thr_prime,(void *)i);
if (err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
}
for (size_t i = LEFT; i < RIGHT; i++)
{
pthread_join(tid[i-LEFT],NULL);
}
exit(0);
}
static void *thr_prime(void *p)
{
int i,j,mark;
mark = 1;
i = (int)p;
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);
}
线程的收尸
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#define LEFT 30000000
#define RIGHT 30000200
#define THRNUM (RIGHT-LEFT+1)
struct thr_arg_t
{
int n;
};
static void *thr_prime(void *p);
int main()
{
int err,i,j,mark;
pthread_t tid[THRNUM];
struct thr_arg_t *p;
void *ptr;
for ( i = LEFT; i <= RIGHT; i++)
{
p = malloc(sizeof(*p));
if (p == NULL)
{
perror("malloc()");
exit(1);
}
p->n = i;
err = pthread_create(tid+(i-LEFT),NULL,thr_prime,p);
if (err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
}
for (size_t i = LEFT; i < RIGHT; i++)
{
//收尸的时候收回来ptr。
pthread_join(tid[i-LEFT],&ptr);
free(ptr);
}
exit(0);
}
static void *thr_prime(void *p)
{
int i,j,mark;
mark = 1;
i = ((struct thr_arg_t *)p)->n;
//free(p);
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(p);
}
线程的取消选项
线程取消:pthread_cancel();
取消有两种状态:允许和不允许
允许取消又分为:异步取消cancel和推迟取消cancel(默认)–推迟到cancel点再响应。
cancel点: POSIX定义的cancel点都是可能引发阻塞的系统调用。
pthread_setcanceltype();//设置取消方式
pthread_setcancelstate();//设置是否允许取消。
pthread_testcancel():本函数什么都不做,就是一个取消点。
线程分离
pthread_detach();//无法收尸join。
3、线程间同步 :互斥量、读写锁
互斥量:
pthread_mutex_t
//互斥量初始化
pthread_mutex_init();
//互斥量销毁
pthread_mutex_destory();
//加锁
pthread_mutex_lock();
//非阻塞加锁
pthread_mutex_trylock();
//解锁
pthread_mutex_unlock();
//一次性初始化
pthread_once();//只被调用一次
互斥量示例
由于互斥量的存在,导致临界区只允许一个线程访问,避免了线程直接的竞争而导致的数据不准确,最终打印结果是20000000.如果不添加互斥量,最后的结果是一个随机数。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
static int g_count = 0;
static pthread_mutex_t mut;
static void *thr_handler(void *p)
{
int i,l_count;;
int loops = *((int*)p);
for ( i = 0; i < loops; i++)
{
pthread_mutex_lock(&mut); //互斥锁上锁
l_count = g_count;
l_count++;
g_count = l_count;
pthread_mutex_unlock(&mut); //互斥锁解锁
}
return (void*)0;
}
static int loops;
int main(int argc,char **argv)
{
pthread_t tid1,tid2;//线程
int ret;
if (argc < 2)
loops = 10000000;
else
loops = atoi(argv[1]);
pthread_mutex_init(&mut, NULL);
ret = pthread_create(&tid1,NULL,thr_handler,&loops);
if (ret)
{
perror("pthread_create()");
exit(1);
}
ret = pthread_create(&tid2,NULL,thr_handler,&loops);
if (ret)
{
perror("pthread_create()");
exit(1);
}
ret = pthread_join(tid1,NULL); //等待线程退出
if (ret)
{
fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
exit(1);
}
ret = pthread_join(tid2,NULL); //等待线程退出
if (ret)
{
fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
exit(1);
}
printf("g_count = %d\n",g_count);
exit(0);
}
条件变量示例
条件变量是线程可用的另一种同步机制。条件变量用于自动阻塞线程,直到某个特定事件发生或某个条件满足为止,通常情况下,条件变量是和互斥锁一起搭配使用的。使用条件变量主要包括两个动作:
⚫ 一个线程等待某个条件满足而被阻塞;
⚫ 另一个线程中,条件满足时发出“信号”
条件变量使用 pthread_cond_t 数据类型来表示,类似于互斥锁,在使用条件变量之前必须对其进行初始
化。初始化方式同样也有两种:使用宏 PTHREAD_COND_INITIALIZER 或者使用函数 pthread_cond_init();
条件变量:pthread_con_t
pthread_cond_init();
pthread_cond_destory();
pthread_cond_broadcast();
pthread_cond_signal();
pthread_cond_wait();
pthread_cond_timewait();
对于初始化与销毁操作,有以下问题需要注意:
⚫ 在使用条件变量之前必须对条件变量进行初始化操作,使用 PTHREAD_COND_INITIALIZER 宏或者函数 pthread_cond_init()都行;
⚫ 对已经初始化的条件变量再次进行初始化,将可能会导致未定义行为;
⚫ 对没有进行初始化的条件变量进行销毁,也将可能会导致未定义行为;
⚫ 对某个条件变量而言,仅当没有任何线程等待它时,将其销毁才是最安全的;
⚫ 经 pthread_cond_destroy()销毁的条件变量,可以再次调用 pthread_cond_init()对其进行重新初始化。
信号量: P216 栗子 mysem
读写锁:写锁 互斥锁 读锁 共享锁
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#define THRNUM 20
#define FNAME "/tmp/out"
#define LINESIZE 1024
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
char linebuf[LINESIZE];
static void *thr_add(void* p)
{
FILE *fp;
fp = fopen(FNAME,"r+");
if (fp == NULL)
{
perror("fopen()");
exit(1);
}
//互斥量
pthread_mutex_lock(&mut);
fgets(linebuf,LINESIZE,fp);
fseek(fp,0,SEEK_SET);
sleep(1);
fprintf(fp,"%d\n",atoi(linebuf)+1);
fclose(fp);
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
int main()
{
int err,i;
pthread_t tid[THRNUM];
for ( i = 0; i < THRNUM; i++)
{
err = pthread_create(tid+i,NULL,thr_add,NULL);
if (err)
{
fprintf(stderr,"pthread_create():%sfailed\n",strerror(err));
exit(1);
}
}
for ( i = 0; i < THRNUM; i++)
{
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&mut);
exit(0);
}
创建线程循环打印abcd,3s之后被alarm信号杀死
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#define THRNUM 4
#define FNAME "/tmp/out"
#define LINESIZE 1024
static pthread_mutex_t mut[THRNUM];
char linebuf[LINESIZE];
static int next(int n)
{
if(n + 1 ==THRNUM)
return 0;
return n+1;
}
static void *thr_add(void* p)
{
int n = (int)p;
int c = 'a' + (int)p;
while (1)
{
pthread_mutex_lock(mut+n);
write(1,&c,1);
pthread_mutex_unlock(mut+next(n));
}
pthread_exit(NULL);
}
int main()
{
int err,i;
pthread_t tid[THRNUM];
for ( i = 0; i < THRNUM; i++)
{
pthread_mutex_init(mut+i,NULL);
pthread_mutex_lock(mut+i);
err = pthread_create(tid+i,NULL,thr_add,(void *)i);
if (err)
{
fprintf(stderr,"pthread_create():%sfailed\n",strerror(err));
exit(1);
}
}
pthread_mutex_unlock(mut+0);
alarm(3);
for ( i = 0; i < THRNUM; i++)
{
pthread_join(tid,NULL);
}
exit(0);
}
多线程实现令牌桶
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include "mytbf.h"
//static __sighandler_t alarm_handler_save;
static struct mytbf_st* job[MYTBF_MAX];
/*互斥量的初始化*/
static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;
static int inited = 0;
static pthread_t tid_alrm;
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
struct mytbf_st
{
int cps;//每秒传输的字节数
int burst;//传输的上限
int token;//令牌桶
int pos;//下标
pthread_mutex_t mut; //互斥量
};
static void *thr_alrm(void *p)
{
int i;
//alarm(1);
while (1)
{
pthread_mutex_lock(&mut_job);
for (i = 0; i < MYTBF_MAX; i++)
{
if (job[i] != NULL)
{
pthread_mutex_lock(&job[i]->mut);
job[i]->token += job[i]->cps;
if (job[i]->token>job[i]->burst)
job[i]->token = job[i]->burst;
pthread_mutex_unlock(&job[i]->mut);
}
}
pthread_mutex_unlock(&mut_job);
sleep(1);
}
}
/*模块卸载,保持进出的状态都一致*/
static void module_unload(void)
{
int i;
/*恢复功能*/
pthread_cancel(tid_alrm);
pthread_join(tid_alrm,NULL);
for (i = 0; i < MYTBF_MAX; i++)
{
if (job[i]!=NULL)
{
mytbf_destory(job[i]);
}
pthread_mutex_destroy(&mut_job);
}
}
static void module_load(void)
{
//alarm_handler_save = signal(SIGALRM,alarm_handler);
//alarm(1);
int err;
err = pthread_create(&tid_alrm,NULL,thr_alrm,NULL);
if (err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
atexit(module_unload);
}
static int get_free_pos_unlocked(void)
{
int i;
for (i = 0; i < MYTBF_MAX; i++)
{
if (job[i] == NULL)
return i;
}
return -1;
}
static int min(int a,int b)
{
if (a<b)
return a;
return b;
}
mytbf_t *mytbf_init(int cps,int burst)
{
struct mytbf_st *me;
int pos;
/*
if (!inited)
{
module_load();
inited = 1;
}
*/
pthread_once(&init_once,module_load);
me = malloc(sizeof(*me));
if (me == NULL)
return NULL;
me->token = 0;
me->cps = cps;
me->burst = burst;
pthread_mutex_init(&me->mut,NULL);
pthread_mutex_lock(&mut_job);
/*临界区函数跳转*/
pos = get_free_pos_unlocked();
if (pos < 0)
{
pthread_mutex_unlock(&mut_job);
free(me);
return NULL;
}
me->pos = pos;
job[pos] = me;
pthread_mutex_unlock(&mut_job);
return me;
}
/*取令牌*/
int mytbf_fetchtoken(mytbf_t *ptr,int size)
{
struct mytbf_st *me = ptr;//指针转换
int n;
if (size <= 0)
return -EINVAL;
pthread_mutex_lock(&me->mut);
while(me->token <= 0)
{
pthread_mutex_unlock(&me->mut);
sched_yield();
pthread_mutex_lock(&me->mut);
}
n = min(me->token,size);
me->token -= n;
pthread_mutex_unlock(&me->mut);
return n;
}
/*归还令牌*/
int mytbf_returntoken(mytbf_t *ptr,int size)
{
struct mytbf_st *me = ptr;//指针转换
if (size <= 0)
return -EINVAL;
pthread_mutex_lock(&me->mut);
me->token += size;
if (me->token > me->burst)
me->token = me->burst;
pthread_mutex_unlock(&me->mut);
return size;
}
int mytbf_destory(mytbf_t *ptr)
{
struct mytbf_st *me = ptr;//指针转换
pthread_mutex_lock(&mut_job);
job[me->pos] = NULL;
pthread_mutex_unlock(&mut_job);
pthread_mutex_destroy(&me->mut);
free(ptr);
return 0;
}
信号量: P216 栗子 mysem
#ifndef MYSEM_H_
#define MYSEM_H_
typedef void mysem_t;
mysem_t *mysem_init(int initval);
int mysem_add(mysem_t *,int);
int mysem_sub(mysem_t *,int);
int mysem_destory(mysem_t *);
#endif // !MYSEM_H_
#include "mysem.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
struct mysem_st
{
int value;
pthread_mutex_t mut;
pthread_cond_t cond;
};
mysem_t *mysem_init(int initval)
{
struct mysem_st *me;
me = malloc(sizeof(*me));
if (me == NULL)
return NULL;
me->value = initval;
pthread_mutex_init(&me->mut,NULL);
pthread_cond_init(&me->cond,NULL);
return me;
}
int mysem_add(mysem_t *ptr,int n)
{
struct mysem_st *me = ptr;
pthread_mutex_lock(&me->mut);
me->value += n;
pthread_cond_broadcast(&me->cond);
pthread_mutex_unlock(&me->mut);
return n;
}
int mysem_sub(mysem_t *ptr,int n)
{
struct mysem_st *me = ptr;
pthread_mutex_lock(&me->mut);
while (me->value < n)
pthread_cond_wait(&me->cond,&me->mut);
me->value -= n;
pthread_mutex_unlock(&me->mut);
return n;
}
int mysem_destory(mysem_t *ptr)
{
struct mysem_st *me = ptr;
pthread_mutex_destroy(&me->mut);
pthread_cond_destroy(&me->cond);
free(me);
}
通知和等待条件变量
条件变量的主要操作便是发送信号(signal)和等待。发送信号操作即是通知一个或多个处于等待状态
的线程,某个共享变量的状态已经改变,这些处于等待状态的线程收到通知之后便会被唤醒,唤醒之后再检
查条件是否满足。等待操作是指在收到一个通知前一直处于阻塞状态。
函数 pthread_cond_signal()和 pthread_cond_broadcast()均可向指定的条件变量发送信号,通知一个或多
个处于等待状态的线程。调用 pthread_cond_wait()函数是线程阻塞,直到收到条件变量的通知。
4、线程相关的属性、线程同步的属性
pthread_attr_init();
pthread_attr_destory();
pthread_attr_setstacksize(&attr,1024*1024);
手册的 man pthread_attr_init(); 的see also
进程与线程 的效率问题 P216
线程同步的属性
互斥量属性:
pthread_mutexattr_init();
pthread_mutexattr_destory();
pthread_mutexattr_getpshared();对互斥量是否跨进程起作用设置属性。
pthread_mutexattr_setpshared();
clone();
pthread_mutexattr_gettype();
pthread_mutexattr_settype();
条件变量:pthread_condattr_init();
pthread_condattr_dsetory();
手册的 pthread_condattr_init(); 的see also
读写锁属性:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
void *func(void *p)
{
while (1)
pause();
}
int main()
{
int err,i;
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr,1024*1024);
for (i = 0;; i++)
{
err = pthread_create(&tid,NULL,func,NULL);
if (err)
{
fprintf(stderr,"pthread_create()%s failed\n",strerror(err));
break;
}
}
printf("%d\n",i);
pthread_attr_destroy(&attr);
exit(0);
}
5、重入、线程和信号的关系、线程与fork的关系
多线程中的IO。
线程和信号的关系
pthread_sigmasl();
sigwait();
线程发信号 pthread_kill
线程与fork的关系
openmp --> www.openmp.org参考内容。