C语言基础-线程
线程概念
进程:
- 进程有独立的地址空间
- Linux为每个进程创建task_structt
- 每个进程都参与内核调度,互不影响
线程:
- 进程在切换时系统开销大
- 很多操作系统引入轻量级进程LWP
- 同一进程中的线程共享相同地址空间
- linux不区分进程、线程
线程资源
- 共享资源
- 可执行的命令
- 静态数据
- 进程中打开的文件描述符
- 当前工作目录
- 用户ID
- 用户组ID
- 私有资源
- 线程ID(TID)
- PC(程序计数器)和相关寄存器
- 堆栈
- 错误号(errno)
- 优先级
- 执行状态和属性
使用线程的好处
- 大大提高了任务切换的效率
- 避免了额外的TLB&cache的刷新
Linux线程库-pthread
如果进程结束,进程下的所有线程都会被结束
线程的创建-pthread_create
#include <pthread.h> int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(routine)(void *),void *arg); // 成功返回0 // 失败返回错误码 // tread线程对象 // attr 线程属性,NULL代表默认属性 // *(routine)(void *) routine线程执行的函数 // arg传递给现场执行函数的参数
线程的回收-pthread_join
#include <pthread.h> int pthread_join(pthread_t thread,void **retval); // 成功返回0 // 失败时返回错误码 // thread 要回收的线程对象 // *retval 接受线程thread的返回值 // 调用线程阻塞知道thread的返回值
线程的结束-pthread_exit
#include <pthread.h> void pthread_exit(void *retval); // 结束当前线程 或指定到函数的return结束 // retval可被其他线程通过pthread_join获取 // retval不能是局部变量否则会出错 // 线程私有资源被释放
Demo:
#include <pthread.h> #include <stdio.h> #include <zconf.h> #include <string.h> char message[32] = "hello World"; void *thread_func(void *arg) { sleep(1); strcpy(message, "thread hello"); // 线程退出 // pthread_exit("thread over"); return "thread over"; // 与上方pthread_exit效果相同 } int main(int argc, char *argv[]) { pthread_t a_thread; void *result; // 创建线程 if (pthread_create(&a_thread, NULL, thread_func, NULL) != 0) { perror("pthread_create"); } // 线程回收 pthread_join(a_thread, &result); printf("result:%s\n", result); // result接受线程的返回值 // 查看线程是否继承了进程的资源 printf("message:%s\n", message); return 0; } // Ps:编译一定要链接 否则会报错 // $gcc -o test test.c -l pthread
线程间通讯
优点:线程间通讯很容易(通过全局变量交换数据)
缺点:多个线程 访问共享数据时需要同步或互斥机制
同步机制
同步(synchronization)指的是多个任务按照约定的先后次序相互配合完成一件事
由信号量来决定线程是继续运行还是阻塞等待
信号量
- 信号量代表某一类资源,其值表示系统中该资源的数量
- 信号量是一个受保护的变量,只能通过三种操作来访问
- 初始化
- P操作(申请资源)
- V操作(释放资源)
信号量P/V操作
- P(S)含义如下
if (信号量的值大于0){申请资源的任务继续运行;信号量的值减一;} else{申请资源的任务阻塞;}
- V(S)含义如下
if(有任务在等待资源){唤醒等待任务,让其继续运行}
Posix信号量
- 无名信号量(基于内存的信号量)
- 有名信号量(既可以用线程,也可以用于进程)
信号量的初始化-sem_init
#include <semaphore.h> int sem_init(sem_t *sem,int pshared,unsigned int val); // 信号量被创建后必须初始化 // 成功时返回0 // 失败返回EOF // sem 指向要初始化的信号量对象 // pshared 信号量的使用范围 0代表线程间 1代表进程间 // val信号量的初值,0代表没有资源,大于0代表有资源
P/V操作
#include <semaphore.h> int sem_wait(sem_t *sem); // P操作 int sem_post(sem_t *sem); // V操作 // 成功时返回0 // 失败时返回EOF // sem执行要操作的信号量对象
Demo1:两个线程同步读写缓冲区(生产者、消费者问题)
#include <pthread.h> #include <stdio.h> #include <zconf.h> #include <string.h> #include <semaphore.h> // 全局buf char buf[32]; sem_t ser; sem_t sew; void *function(void *args) { while (1) { // 执行p操作 sem_wait(&ser); printf("you enter %d charachtes\n", strlen(buf)); sem_post(&sew); } }; int main(int argc, char *argv[]) { pthread_t a_thread; // 先初始化信号量 写和读 if (sem_init(&ser, 0, 0) < 0) { perror("sem_init"); return 0; } // 这里初始化为1 if (sem_init(&sew, 0, 1) < 0) { perror("sem_init"); return 0; } // 再创建线程 if (pthread_create(&a_thread, NULL, function, NULL) != 0) { printf("no create"); return 0; } printf("input quit to exit:\n"); do { // 每次往缓冲区写完一次操作后都将执行一次post v操作操作 sem_wait(&sew); fgets(buf, 32, stdin); sem_post(&ser); } while (strncmp(buf, "quit", 4) != 0); { // 如果输入不是quit则继续循环 return 0; } }
互斥机制
临界资源
- 一次只允许一个任务(进程、现场)访问的共享资源
临界区
- 访问临界的代码
互斥机制
- mutex互斥锁
- 任务访问临界资源前申请锁,访问完后释放锁
初始化锁-pthread_mutex_init
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr); // 成功时返回0 // 失败时返回错误码 // mutex 指向要初始化的互斥锁对象 // attr互斥锁属性,NULL表示缺省属性
申请锁-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指向要初始化的互斥锁对象
Demo:
#include <pthread.h> #include <stdio.h> #include <zconf.h> unsigned int value1, value2; pthread_mutex_t lock; int number = 0; void *function() { pthread_mutex_lock(&lock); puts("子线程进入"); value1 = number++; value2 = ++number; sleep(1); puts("子线程截止"); printf("value1: %d value2:%d\n", value1, value2); pthread_mutex_unlock(&lock); return 0; } int main(int argc, char *argv[]) { pthread_t a_thread; // 初始化锁 if (pthread_mutex_init(&lock, NULL) != 0) { perror("pthread_mutex_init"); return 0; } // 创建线程 if (pthread_create(&a_thread, NULL, function, NULL) != 0) { perror("pthread_create"); return 0; } // 加锁 pthread_mutex_lock(&lock); puts("主进程进入"); value1 = number++; value2 = ++number; sleep(1); puts("主进程截止"); printf("value1: %d value2:%d\n", value1, value2); pthread_mutex_unlock(&lock); sleep(3); }
Songzhibin