目录
1、创建线程
系统调用的最小单位
线程标识
pthread_t (有可能是一个结构)
pthread_self(3); 用来返回调用线程的 ID
pthread_equal(3); 比较两个线程标识符
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
pthread_t *thread,指向线程标识符的指针
const pthread_attr_t *attr,设置线程属性
void *(*start_routine) (void *), 线程运行函数的起始地址
void *arg运行函数的参数
功能:创建一个线程,在线程创建以后,就开始运行相关的线程函数
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*
体验多线程
线程就是独立运行的函数
*/
static void *thr_fun(void *s)
{
for (int i = 0; i < 20; i++) {
write(1, "!", 1);
sleep(1);
}
// pthread_exit(0);
return (void *)0;
}
int main(void)
{
// main线程
pthread_t tid;
int err = pthread_create(&tid, NULL, thr_fun, NULL);
if (err) {
fprintf(stderr, "pthread_create():%s\n", strerror(err));
exit(1);
}
for (int i = 0; i < 10; i++) {
write(1, "*", 1);
sleep(1);
}
// return 0;
pthread_exit(0);
}
2、同一个进程中的多个线程共享的资源
共享
pid ppid 终端 信号行为 文件表项 环境表
用户和组 控制终端 文件屏蔽字 记录锁
全局数据段 进程组和会话 资源限制
不同
tid 栈 信号屏蔽字
3、线程的终止
从启动例程返回
pthread_exit(3);
被取消
4、等待线程结束
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
以阻塞的方式等待thread指定的线程结束
retval: 用户定义的指针,用来存储被等待线程的返回值
线程没有返回值,又或者我们不需要接收线程的返回值,可以将 retval 参数置为 NULL
5、同步
临界区 多线程竞争的那段代码
互斥量 保住多线程在执行临界区代码同步
常用接口
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
初始化,以动态方式创建互斥锁,参数attr指定了新建互斥锁的属性。 如果参数attr为空,则使用默认的 互斥锁 属性,默认属性为快速互斥锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_mutex_lock这个操作是阻塞调用的,也就是说,如果这个锁此时正在被其它线程占用, 那么 pthread_mutex_lock () 调用会进入到这个锁的排队队列中,并会进入阻塞状态, 直到拿到锁之后才会返回。
pthread_mutex_unlock(3);该函数用来释放互斥体引用的互斥对象。释放互斥锁的方式取决于互斥锁的类型属性。
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
用于注销一个互斥锁
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define START 100
#define END 300
#define THNR 3
/*
job > 0 已分配的数值
job == 0 分配的数值已被任务线程取走
job == -1 所有发放的任务已完成
所有的线程都要存取job变量,保证同步
*/
static int job;
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
static int isPrimer(int n);
static void *thr_job(void *s);
int main(void)
{
// 构建线程---》发放任务---》收尸
pthread_t tids[THNR] = {};
int i, err;
for (i = 0; i < THNR; i++) {
err = pthread_create(tids + i, NULL, thr_job, NULL);
if (err) {
fprintf(stderr, "pthread_create():%s\n", strerror(err));
exit(1);
}
}
for (i = START; i <= END; i++) {
pthread_mutex_lock(&mut);
while (job > 0) {
// 上一次发放的任务还未被线程取走
pthread_mutex_unlock(&mut);
pthread_mutex_lock(&mut);
}
job = i;
pthread_mutex_unlock(&mut);
}
// 最后一个任务被取走了。。。。job = -1
pthread_mutex_lock(&mut);
while (job > 0) {
pthread_mutex_unlock(&mut);
pthread_mutex_lock(&mut);
}
job = -1;
pthread_mutex_unlock(&mut);
for (i = 0; i < THNR; i++)
pthread_join(tids[i], NULL);
pthread_mutex_destroy(&mut);
exit(0);
}
/*
任务线程
*/
static void *thr_job(void *s)
{
int num;
// 循环取任务---》做任务(筛质数)--->终止条件job = -1
while (1) {
pthread_mutex_lock(&mut);
if (job == -1) {
pthread_mutex_unlock(&mut);
break;
}
if (job == 0) {
// 任务还未发放
pthread_mutex_unlock(&mut);
continue;
}
num = job;
job = 0;
pthread_mutex_unlock(&mut);
// 以下不竞争
if (isPrimer(num))
printf("%d is a primer\n", num);
}
pthread_exit(0);
}
static int isPrimer(int n)
{
for (int i = 2; i < n; i++)
if (n % i == 0)
return 0;
return 1;
}
6、条件变量
多线程在竞争资源,获取条件采用通知法
第一步:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
初始化pthread_cond_t类型的条件变量
第二步:
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_signal:发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
pthread_cond_broadcast:函数会将所有等待该条件变量的线程解锁而不是仅仅解锁一个线程
第三步:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
阻塞等待条件变量cond
释放已掌握的互斥锁(解锁互斥量)相当于ptherad_mutex_unlock(&mutex);
当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁,相当于pthread_mutex_lock(&mutex);
第四步:
int pthread_cond_destroy(pthread_cond_t *cond);
用于销毁一个条件变量
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define START 100
#define END 300
#define THNR 3
/*
job > 0 已分配的数值
job == 0 分配的数值已被任务线程取走
job == -1 所有发放的任务已完成
所有的线程都要存取job变量,保证同步
条件变量
pthread_cond_t
pthread_cond_init(3)
pthread_cond_wait(3);
pthread_cond_signal(3) / pthread_cond_broast(3)
pthread_cond_destroy(3);
*/
static int job;
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int isPrimer(int n);
static void *thr_job(void *s);
int main(void)
{
// 构建线程---》发放任务---》收尸
pthread_t tids[THNR] = {};
int i, err;
for (i = 0; i < THNR; i++) {
err = pthread_create(tids + i, NULL, thr_job, NULL);
if (err) {
fprintf(stderr, "pthread_create():%s\n", strerror(err));
exit(1);
}
}
for (i = START; i <= END; i++) {
pthread_mutex_lock(&mut);
// 轮询----》通知
while (job != 0) {
// 解锁并等待条件变化通知的到来,一旦接收到通知抢锁
pthread_cond_wait(&cond, &mut);
}
job = i;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mut);
}
// 最后一个任务被取走了。。。。job = -1
pthread_mutex_lock(&mut);
while (job != 0) {
pthread_cond_wait(&cond, &mut);
}
job = -1;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
for (i = 0; i < THNR; i++)
pthread_join(tids[i], NULL);
pthread_mutex_destroy(&mut);
pthread_cond_destroy(&cond);
exit(0);
}
/*
任务线程
*/
static void *thr_job(void *s)
{
int num;
// 循环取任务---》做任务(筛质数)--->终止条件job = -1
while (1) {
pthread_mutex_lock(&mut);
while (job == 0) {
// 任务还未发放
pthread_cond_wait(&cond, &mut);
}
if (job == -1) {
pthread_mutex_unlock(&mut);
break;
}
num = job;
job = 0;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
// 以下不竞争
if (isPrimer(num))
printf("%d is a primer\n", num);
}
pthread_exit(0);
}
static int isPrimer(int n)
{
for (int i = 2; i < n; i++)
if (n % i == 0)
return 0;
return 1;
}
7、取消
State-->whether Type--->when
默认state是enabled,默认的type是defferred
pthread_cancel();
取消点函数(man 7 pthreads)
接受到取消请求后
调用注册的终止处理程序
pthread_cleanup_push(3) / pthread_cleanup_pop(3)
线程终止
8、线程与io
pread(3) / pwrite(3)替代read(2) / write(2)
9、线程与信号
每一个线程都有自己的mask位图(pthread_sigmask(3))
多线程大量与信号混用不推荐(难控制)
多线程处理信号,专门有一个线程处理,其他线程屏蔽