网站建设哪些模板号,开平建设局网站,线上拓客渠道有哪些,网站404页面编写目录
编辑
一、多进程与多线程对比
二、 临界资源 临界区 竞态 例1#xff1a;临界资源 实现 输入输出 例2#xff1a;对临界资源 进行 减减
例子3#xff1a;临界资源抢占使用
三、线程的同步互斥机制#xff08;用于解决竟态#xff09;
3.1基本概念
3.2线…
目录
编辑
一、多进程与多线程对比
二、 临界资源 临界区 竞态 例1临界资源 实现 输入输出 例2对临界资源 进行 减减
例子3临界资源抢占使用
三、线程的同步互斥机制用于解决竟态
3.1基本概念
3.2线程互斥
1、在C语言中线程的互斥通过互斥锁来完成
2、互斥锁本质上是一种临界资源但互斥锁在同一时刻只能被一个线程使用当一个线程试图去锁定另一个线程锁定的互斥所时该线程会阻塞等待直到使用互斥锁的线程解锁了该互斥锁
3、互斥锁的相关API 4、对例3进行 互斥
3.3死锁 3.4线程同步
3.5线程同步 无名信号量
3.6 线程同步 条件变量 一、多进程与多线程对比 二、 临界资源 临界区 竞态 例1临界资源 实现 输入输出
#include myhead.h
#define N 2char buf[128] ;//临界资源// 定义线程体1 使用临界资源的代码 称为临界区
void *task1(void *arg)
{while (1) // 输入信息{printf(输入);fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] 0;}
}// 定义线程体2 使用临界资源的代码 称为临界区
void *task2(void *arg)
{while (1) // 循环输出信息{usleep(500000); // 500000us 0.5s 执行一次printf(buf %s\n, buf);bzero(buf, sizeof(buf));}
}
int main(int argc, char const *argv[])
{// 创建两个任务pthread_t tid1, tid2;if (pthread_create(tid1, NULL, task1, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid2, NULL, task2, NULL) ! 0){printf(task2 error);return -1;}// 主程序printf(tid1 %#lx, tid2 %#lx\n, tid1, tid2);// 阻塞回收线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}例2对临界资源 进行 减减
#include myhead.h
#define N 2int num 520;//临界资源// 定义线程体1 使用临界资源的代码 称为临界区
void *task1(void *arg)
{while (1) // 输入信息{if (num 0){num--;printf(task1 执行一次剩余%d次\n,num);usleep(1000);}else{printf(task1 执行结束\n);break;}}pthread_exit(EXIT_SUCCESS);
}// 定义线程体2 使用临界资源的代码 称为临界区
void *task2(void *arg)
{while (1) // 输入信息{if (num 0){num--;printf(task2 执行一次剩余%d次\n,num);usleep(1000);}else{printf(task2 执行结束\n);break;}}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{// 创建两个任务pthread_t tid1, tid2;if (pthread_create(tid1, NULL, task1, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid2, NULL, task2, NULL) ! 0){printf(task2 error);return -1;}// 主程序printf(tid1 %#lx, tid2 %#lx\n, tid1, tid2);// 阻塞回收线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}例子3临界资源抢占使用
#include myhead.h
#define N 2int num 520;//临界资源// 定义线程体1 使用临界资源的函数 称为临界区
void *task1(void *arg)
{while (1) // 输入信息{if (num 0){num--;printf(task1 执行一次剩余%d次\n,num);usleep(1000);}else{printf(task1 执行结束\n);break;}}pthread_exit(EXIT_SUCCESS);
}// 定义线程体2 使用临界资源的函数 称为临界区
void *task2(void *arg)
{while (1) // 输入信息{if (num 0){num--;printf(task2 执行一次剩余%d次\n,num);usleep(1000);}else{printf(task2 执行结束\n);break;}}pthread_exit(EXIT_SUCCESS);
}// 定义线程体1 使用临界资源的函数 称为临界区
void *task3(void *arg)
{while (1) // 输入信息{if (num 0){num--;printf(task3 执行一次剩余%d次\n,num);usleep(1000);}else{printf(task3 执行结束\n);break;}}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{// 创建三个任务pthread_t tid1, tid2,tid3;if (pthread_create(tid1, NULL, task1, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid2, NULL, task2, NULL) ! 0){printf(task2 error);return -1;}if (pthread_create(tid3, NULL, task3, NULL) ! 0){printf(task2 error);return -1;}// 主程序printf(tid1 %#lx, tid2 %#lx\n, tid1, tid2);// 阻塞回收线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);return 0;
}由于多个线程共同使用进程的资源导致线程在操作上容易出现不安全的状态 对于例3 减减 含有多步操作 相对耗时 在多个线程对临界资源抢占使用时会出现某一个线程的 减减 未结束另一个线程的 开始执行就会导致数据出错。 该现象称为 竞态
三、线程的同步互斥机制用于解决竟态 如上例3所示当其中一个线程正在访问全局变量时可能会出现时间片用完的情况另一个线程将数据进行更改后再执行 该线程时导致数据的不一致。这种现象称竞态为了解决竞态引入了同步互斥机制
3.1基本概念
1竟态同一个进程的多个线程在访问临界资源时会出现抢占的现象导致线程中的数据错误的现象称为竞态
2临界资源多个线程共同访问的数据称为临界资源可以是全局变量、堆区数据、外部文件
3临界区访问临界资源的代码段称为临界区
4粒度临界区的大小
5同步表示多个任务有先后顺序的执行
6互斥表示在访问临界区时同一时刻只能有一个任务当有任务在访问临界区时其他任务只能等待
3.2线程互斥
1、在C语言中线程的互斥通过互斥锁来完成
2、互斥锁本质上是一种临界资源但互斥锁在同一时刻只能被一个线程使用当一个线程试图去锁定另一个线程锁定的互斥所时该线程会阻塞等待直到使用互斥锁的线程解锁了该互斥锁
3、互斥锁的相关API #include pthread.h 1、创建互斥锁只需要定义一个pthread_mutex_t类型的变量就创建了一个互斥锁 pthread_mutex_t fastmutex PTHREAD_MUTEX_INITIALIZER; //静态初始化一个互斥锁 2、初始化互斥锁 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); 功能初始化一个互斥锁 参数1互斥锁变量的地址 参数2互斥锁的属性一般填NULL使用系统默认提供的属性 返回值总是成功不会失败成功返回0 3、获取锁资源 int pthread_mutex_lock(pthread_mutex_t *mutex); 功能某个线程调用该函数表示获取锁资源阻塞等待 参数互斥锁的地址 返回值成功返回0失败返回一个错误码 4、释放锁资源 int pthread_mutex_unlock(pthread_mutex_t *mutex); 功能将线程中的锁资源释放 参数互斥锁的地址 返回值成功返回0失败返回一个错误码 5、销毁锁资源 int pthread_mutex_destroy(pthread_mutex_t *mutex); 功能销毁程序中的锁资源 参数互斥锁地址 4、对例3进行 互斥
#include myhead.h
#define N 2int num 520; // 临界资源// 创建一个互斥锁
pthread_mutex_t mutex;// 定义线程体1 使用临界资源的代码 称为临界区
void *task1(void *arg)
{while (1) // 输入信息{// 上锁 获取锁资源pthread_mutex_lock(mutex);if (num 0){num--;printf(task1 执行一次剩余%d次\n, num);usleep(1000);}else{printf(task1 执行结束\n);// 解锁 释放锁资源pthread_mutex_unlock(mutex); // 结束前解锁 避免死锁break;}// 解锁 释放锁资源pthread_mutex_unlock(mutex);}pthread_exit(EXIT_SUCCESS);
}// 定义线程体2 使用临界资源的代码 称为临界区
void *task2(void *arg)
{while (1) // 输入信息{// 上锁 获取锁资源pthread_mutex_lock(mutex);if (num 0){num--;printf(task2 执行一次剩余%d次\n, num);usleep(1000);}else{printf(task2 执行结束\n);// 解锁 释放锁资源pthread_mutex_unlock(mutex); // 避免死锁break;}// 解锁 释放锁资源pthread_mutex_unlock(mutex);}pthread_exit(EXIT_SUCCESS);
}// 定义线程体3 使用临界资源的函数 称为临界区
void *task3(void *arg)
{while (1) // 输入信息{// 上锁 获取锁资源pthread_mutex_lock(mutex);if (num 0){num--;printf(task3 执行一次剩余%d次\n, num);usleep(1000);}else{printf(task3 执行结束\n);// 解锁 释放锁资源pthread_mutex_unlock(mutex); // 避免死锁break;}// 解锁 释放锁资源pthread_mutex_unlock(mutex);}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{//初始化锁pthread_mutex_init(mutex, NULL);// 创建三个任务pthread_t tid1, tid2, tid3;if (pthread_create(tid1, NULL, task1, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid2, NULL, task2, NULL) ! 0){printf(task2 error);return -1;}if (pthread_create(tid3, NULL, task3, NULL) ! 0){printf(task2 error);return -1;}// 主程序printf(tid1 %#lx, tid2 %#lx\n, tid1, tid2);// 阻塞回收线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);// 销毁锁资源pthread_mutex_destroy(mutex);return 0;
}3.3死锁
1、概念在多线程编程中死锁是一种情况其中两个或多个线程被永久阻塞因为每个线程都在等待其他线程释放它们需要的资源。在C语言中这通常涉及互斥锁(mutexes)当多个互斥锁被不同的线程以不同的顺序获取时很容易发生死锁。
2、产生条件 1 互斥条件资源不能被多个线程共享只能被一个线程在任一时刻所使用。 2 持有和等待条件一个线程至少持有一个资源并且正在等待获取一个当前被其他线程持有的资源。 3.不可抢占条件资源不能被强制从一个线程抢占到另一个线程线程必须自愿释放它的资源。 4.循环等待条件存在一个线程或多个线程的集合{P1, P2, ..., Pn}其中P1正在等待P2持有的资源P2正在等待P3持有的资源依此类推直至Pn正在等待P1持有的资源。 3、例4死锁示例
#include pthread.h
#include stdio.hpthread_mutex_t lock1, lock2;void* thread1(void* arg) {pthread_mutex_lock(lock1);sleep(1); // 确保线程2能锁住lock2pthread_mutex_lock(lock2);printf(Thread 1\n);pthread_mutex_unlock(lock2);pthread_mutex_unlock(lock1);return NULL;
}void* thread2(void* arg) {pthread_mutex_lock(lock2);sleep(1); // 确保线程1能锁住lock1pthread_mutex_lock(lock1);printf(Thread 2\n);pthread_mutex_unlock(lock1);pthread_mutex_unlock(lock2);return NULL;
}int main() {pthread_t t1, t2;pthread_mutex_init(lock1, NULL);pthread_mutex_init(lock2, NULL);pthread_create(t1, NULL, thread1, NULL);pthread_create(t2, NULL, thread2, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_mutex_destroy(lock1);pthread_mutex_destroy(lock2);return 0;
}
4、避免死锁的策略 1.避免持有和等待尽可能让线程在开始执行前一次性获取所有必需的资源。 2.资源排序规定一个全局顺序来获取资源并且强制所有线程按这个顺序获取资源。 3.使用超时在尝试获取资源时使用超时机制这样线程在等待过长时间后可以放弃回退并重新尝试。 4.检测死锁并恢复运行时检测死锁的存在一旦检测到死锁采取措施如终止线程或回滚操作来解决。 3.4线程同步
1、多个线程任务有顺序的执行由于多个任务有顺序的执行了那么在同一时刻对临界资源的访问就只一个线程
2、线程同步文件的经典问题是:生产者消费者问题 对于该问题而言需要先执行生产者任务然后执行消费者任务
3、对于线程同步问题有两种机制来完成:无名信号量、条件变量
3.5线程同步 无名信号量
1、无名信号量本质上也是一个特殊的临界资源
2、无名信号量中维护了一个value值该值表示能够申请的资源个数当该值为0时申请资源的线程将处于阻塞直到其他线程将该无名信号量中的value值增加到大于0时即可
3、无名信号量API 1、创建无名信号量只需要定义一个 sem_t 类型的变量就创建了一个无名信号量 sem_t sem; 2、初始化无名信号量 #include semaphore.h int sem_init(sem_t *sem, int pshared, unsigned int value); 功能完成对无名信号量的初始化工作 参数1要被初始化的无名信号量地址 参数2无名信号量适用情况 0表示多线程之间 非0表示多进程间同步 参数3无名信号量的资源初始值 返回值成功返回0失败返回-1并置位错误码 3、申请资源P操作将资源减1操作 #include semaphore.h int sem_wait(sem_t *sem); 功能申请无名信号量中的资源使得该信号量中的value-1 参数无名信号量地址 返回值成功返回0失败返回-1并置位错误码 4、释放资源V操作将资源加1操作 #include semaphore.h int sem_post(sem_t *sem); 功能将无名信号量中的资源加1操作 参数无名信号量地址 返回值成功返回0失败返回-1并置位错误码 5、销毁无名信号量 #include semaphore.h int sem_destroy(sem_t *sem); 功能销毁一个无名信号量 参数无名信号量地址 返回值成功返回0失败返回-1并置位错误码 4、同步 无名信号量 示例
未 使用 无名信号量
#include myhead.h//创建生产者线程
void * task1(void *arg)
{int num 5;while (num--){printf(%#lx:执行一次生产\n,pthread_self());sleep(1);}pthread_exit(EXIT_SUCCESS);
}//创建消费者线程
void * task2(void *arg)
{int num 5;while (num--){printf(%#lx:执行一次消费\n,pthread_self());}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{pthread_t tid1, tid2;if (pthread_create(tid1, NULL, task1, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid2, NULL, task2, NULL) ! 0){printf(task1 error);return -1;}pthread_join(tid2, NULL);pthread_join(tid1, NULL);return 0;
}使用 无名信号量
#include myhead.h//1、创建无名学号量
sem_t sem;//创建生产者线程
void * task1(void *arg)
{int num 5;while (num--){printf(%#lx:执行一次生产\n,pthread_self());//4、释放资源 无名信号量资源加1 允许消费者a线程执行sem_post(sem);}pthread_exit(EXIT_SUCCESS);
}//创建消费者线程
void * task2(void *arg)
{int num 5;while (num--){//3、申请资源sem_wait(sem); //阻塞等待 生产者线程释放 无名信号量资源//当无名信号量资源 为1时 执行 减1//因为 无名信号量 初始资源为0 即便先执行消费者线程也会被阻塞printf(%#lx:执行一次消费\n,pthread_self());}pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{//2、初始化无名信号量sem_init(sem,0,0);//第一个0 表示同步用于多线程间//第二e个0 表示无名信号量初始资源为0pthread_t tid1, tid2;if (pthread_create(tid1, NULL, task1, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid2, NULL, task2, NULL) ! 0){printf(task1 error);return -1;}pthread_join(tid2, NULL);pthread_join(tid1, NULL);//5、销毁无名信号量sem_destroy(sem);return 0;
}3.6 线程同步 条件变量
1、条件变量的本质也是一个特殊的临界资源
2、条件变量中维护了一个队列想要执行的消费者线程需要先进入等待队列中等生产者线程进行唤醒后依次执行。这样就可以做到一个生产者和多个消费者之间的同步但是消费者和消费者之间在进入等待队列这件事上是互斥的。
3、条件变量的API 1创建条件变量 只需要定义一个pthread_cond_t类型的变量就创建了一个条件变量 pthread_cond_t cond; 2初始化条件变量 int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); 功能初始化一个条件变量 参数1条件变量的地址 参数2条件变量的属性一般填NULL 返回值 成功返回0失败返回非0错误码 3将消费者线程放入到等待队列中 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 功能消费者线程进入等待队列中 参数1条件变量的地址 参数2互斥锁的地址因为多个消费者线程在进入等待队列上是竞态的 返回值 成功返回0失败返回非0错误码 4唤醒消费者线程 int pthread_cond_signal(pthread_cond_t *cond); 功能唤醒等待队列中的第一个消费者线程 参数条件变量的地址 返回值 成功返回0失败返回非0错误码 int pthread_cond_broadcast(pthread_cond_t *cond); 功能唤醒所有等待队列中的消费者线程 参数条件变量的地址 返回值 成功返回0失败返回非0错误码 5销毁条件变量 int pthread_cond_destroy(pthread_cond_t *cond); 功能销毁一个条件变量 参数条件变量的地址 返回值 成功返回0失败返回非0错误码 4、条件变量 示例
#include myhead.h//1、定义一个条件变量
pthread_cond_t cond;//11、创建互斥锁
pthread_mutex_t mutex;// 创建生产者线程
void *task1(void *arg)
{// int num 5;// while (num--)// {// sleep(1);// printf(%#lx:执行一次生产\n, pthread_self());// //4、唤醒消费者线程(单个)// pthread_cond_signal(cond);// }sleep(3);printf(%#lx:执行5次生产\n, pthread_self());pthread_cond_broadcast(cond);//4、唤醒消费者线程(全部)pthread_exit(EXIT_SUCCESS);
}// 创建消费者线程
void *task2(void *arg)
{//33、获取互斥锁pthread_mutex_lock(mutex);//3、请求进入等待队列pthread_cond_wait(cond,mutex);printf(%#lx:执行一次消费\n, pthread_self());//44、释放互斥锁pthread_mutex_unlock(mutex);pthread_exit(EXIT_SUCCESS);
}
int main(int argc, char const *argv[])
{//2、初始化统计变量pthread_cond_init(cond,NULL);//22、初始化互斥锁pthread_mutex_init(mutex,NULL);pthread_t tid1, tid2, tid3, tid4, tid5, tid6;if (pthread_create(tid1, NULL, task1, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid2, NULL, task2, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid3, NULL, task2, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid4, NULL, task2, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid5, NULL, task2, NULL) ! 0){printf(task1 error);return -1;}if (pthread_create(tid6, NULL, task2, NULL) ! 0){printf(task1 error);return -1;}//输出每个线程的线程号printf(tid2%#lx tid3%#lx tid4%#lx tid5%#lx tid6%#lx \n,tid2,tid3,tid4,tid5,tid6);pthread_join(tid2, NULL);pthread_join(tid1, NULL);pthread_join(tid3, NULL);pthread_join(tid4, NULL);pthread_join(tid5, NULL);pthread_join(tid6, NULL);//5、销毁条件变量pthread_cond_destroy(cond);//55、销毁锁资源pthread_mutex_destroy(mutex);return 0;
}