文章目录
线程基础
进程特点
线程
线程特点
线程共享资源
线程私有资源
Linux线程库
线程库功能
线程创建 – pthread_create
线程回收 – pthread_join
线程结束 – pthread_exit
其他函数
线程间通信
同步
信号量
Posix 信号量
信号量初始化 – sem_init
信号量 – P / V 操作
线程通信——互斥
互斥锁初始化 – pthread_mutex_init
申请锁 – pthread_mutex_lock
释放锁 – pthread_mutex_unlock
写在最后
线程基础
QQ的多个功能:
接收输入
通讯
显示界面
为了实现多个功能可以使用线程。
进程特点
进程有独立的地址空间(不能互相访问)
Linux为每个进程创建task_struct
每个进程都参与内核调度,互不影响
线程
进程在切换时系统开销大,很多操作系统引入了轻量级进程LWP
同一进程中的线程共享相同地址空间
Linux不区分进程、线程
线程特点
通常线程指的是共享相同地址空间的多个任务
使用多线程的好处:
大大提高了任务切换的效率
避免了额外的TLB & cache的刷新(进程上下文)
线程共享资源
可执行的指令
静态数据(全局变量)
进程中打开的文件描述符
当前工作目录
用户ID
用户组ID
线程私有资源
线程ID (TID)
PC(程序计数器)和相关寄存器
堆栈
错误号 (errno)
优先级
执行状态和属性
Linux线程库
线程库功能
pthread线程库中提供了如下基本操作
创建线程
回收线程
结束线程
同步和互斥机制
信号量
互斥锁
线程创建 – pthread_create
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*routine)(void *), void *arg);
成功返回0,失败时返回错误码
thread 线程对象
attr 线程属性,NULL代表默认属性
routine 线程执行的函数
arg 传递给routine的参数 ,参数是void * ,注意传递参数格式,
示例代码:
#include <stdio.h> #include <pthread.h> #include <string.h> void * function1(void *arg){ printf("this is thread function\n"); } int main(){ pthread_t tid; int err; err = pthread_create(&tid, NULL, function1, NULL); sleep(1); if(err != 0){ printf("create thread:%s\n",strerror(err)); } return 0; }
注意:编译的时候需要加入-lpthread链接线程库!在线程中exit就会全部结束!
线程回收 – pthread_join
#include <pthread.h> int pthread_join(pthread_t thread, void **retval);//*tid 和线程的返回值
成功返回0,失败时返回错误码
thread 要回收的线程对象
调用线程阻塞直到thread结束
*retval 接收线程thread的返回值
线程结束 – pthread_exit
结束当前线程
retval可被其他线程通过pthread_join获取
线程私有资源被释放
示例代码:
#include <stdio.h> #include <pthread.h> #include <string.h> void * function1(void *arg){ printf("this is thread function\n"); sleep(1); pthread_exit("funct exit"); } int main(){ pthread_t tid; int err, i; err = pthread_create(&tid, NULL, function1, NULL); void *retval; pthread_join(tid, &retval); printf("retval = %s",(char *)retval); return 0; }
其他函数
这部分就自己看man手册尝试用用吧
Pthread_join
Pthread_cancel
pthread_testcancel
pthread_setcancelstate
pthread_setcanceltype
Pthead_detach
pthread_attri_init
取消一个线程
int pthread_cancel(pthread_t thread);
void pthread_testcancel(void);
int pthread_setcancelstate(int state, int *oldstate);
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE
int pthread_setcanceltype(int type, int *oldtype);
PTHREAD_CANCEL_DEFERRED
PTHREAD_CANCEL_ASYNCHRONOUS
线程间通信
线程共享同一进程的地址空间
优点:线程间通信很容易 通过全局变量交换数据
缺点:多个线程访问共享数据时需要同步或互斥机制
同步
同步(synchronization)指的是多个任务按照约定的先后次序相互配合完成一件事情
1968年,Edsgar Dijkstra基于信号量的概念 提出了一种同步机制
由信号量来决定线程是继续运行还是阻塞等待
信号量
信号量代表某一类资源,其值表示系统中该资源的数量
信号量是一个受保护的变量,只能通过三种操作来访问
初始化
P操作(申请资源)
V操作(释放资源)
Posix 信号量
posix中定义了两类信号量:
无名信号量(基于内存的信号量)
有名信号量
信号量初始化 – sem_init
#include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int val);
成功时返回0,失败时EOF
sem 指向要初始化的信号量对象
pshared 0 – 线程间 1 – 进程间
val 信号量初值
信号量 – P / V 操作
#include <semaphore.h> int sem_wait(sem_t *sem); P操作 int sem_post(sem_t *sem); V操作
成功时返回0,失败时返回EOF
sem 指向要操作的信号量对象
示例代码:
#include <stdio.h> #include <pthread.h> #include <string.h> #include <semaphore.h> #include <stdlib.h> char buf[100]; sem_t sem; void *write(void *arg){ while(1){ fgets(buf, 20, stdin); sem_post(&sem); } } void *read(void *arg){ while(1){ sem_wait(&sem); printf("%s\n", buf); memset(buf, 0, sizeof(buf)); } } int main(){ pthread_t tid1, tid2; int re; sem_init(&sem, 0 , 0); re = pthread_create(&tid1, NULL, write, NULL); if(re != 0){ printf("pthread_create: %s\n",strerror(re)); exit(0); } re = pthread_create(&tid2, NULL, read, NULL); if(re != 0){ printf("pthread_create: %s\n",strerror(re)); exit(0); } while(1){ sleep(1); } }
线程通信——互斥
临界资源
一次只允许一个任务(进程、线程)访问的共享资源
临界区
访问临界区的代码
互斥机制
mutex互斥锁
任务访问临界资源前申请锁,访问完后释放锁
互斥锁初始化 – pthread_mutex_init
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr);
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
attr 互斥锁属性,NULL表示缺省属性
man 函数出现 No manual entry for pthread_mutex_xxx解决办法
apt-get install manpages-posix-dev
申请锁 – pthread_mutex_lock
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex);
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
如果无法获得锁,任务阻塞
释放锁 – pthread_mutex_unlock
#include <pthread.h> int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
执行完临界区要及时释放锁
示例代码:
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <semaphore.h> FILE *fp; pthread_mutex_t mutex; void *write1(void* arg){ int a=0; a = (int)arg; int len,i; char *c1 = "Hello world\n"; char *c2; len = strlen(c1); int td = pthread_self(); pthread_detach(pthread_self()); c2 = c1; while(1){ pthread_mutex_lock(&mutex); for(i=0;i<len;i++){ fputc(*c1,fp); fflush(fp); c1++; usleep(10000); } pthread_mutex_unlock(&mutex); c1 = c2; sleep(1); } } void *write2(void* arg){ int a=0; a = (int)arg; int len,i; char *c1 = "How are your\n"; char *c2; c2 = c1; len = strlen(c1); int td = pthread_self(); pthread_detach(pthread_self()); while(1){ pthread_mutex_lock(&mutex); for(i=0;i<len;i++){ fputc(*c1,fp); fflush(fp); c1++; usleep(10000); } pthread_mutex_unlock(&mutex); c1 = c2; sleep(1); } } int main(){ int re,i=0; pthread_t tid1,tid2; fp = fopen("1.txt","w"); if(!fp){ perror("fopen"); return -1; } pthread_mutex_init(&mutex,NULL); re = pthread_create(&tid1, NULL,write1, (void *)i); pthread_detach(tid1); if(re!=0){ printf("pthread_create:%s\n",strerror(re)); exit(0); } re = pthread_create(&tid2, NULL,write2, (void *)i); pthread_detach(tid2); if(re!=0){ printf("pthread_create:%s\n",strerror(re)); exit(0); } while(1){ sleep(1); } }
写在最后
今天讲完了线程相关的内容,这部分内容比较枯燥,建议跟着我的示例代码敲一敲,然后之前我也写过相关的内容建议参照着一起看【C语言有什么用?②】制作一个多线程词频统计工具所有文件我都放在了gitee哦,需要自取,我尽量一天一更,大家和我一起变强呀!明天开始进入进程间通信专题!最后三连即可提高学习效率!!!
另外我在更新的就是算法笔记的一些例题笔记,这个系列是用于提高我的算法能力,如果有兴趣对算法领域感兴趣找不到合适的入门文章也可以追更,如果我更新的太慢了请大家点赞收藏,一键三连才能更有更新的动力呀0.0