家具网站开发目的,重庆网站建设子沃科技公司,往网站上做新东西需要什么,免费编程教学视频一、临界资源与临界区
多线程会共享例如全局变量等资源#xff0c;我们把会被多个执行流访问的资源称为临界资源#xff0c;我们是通过代码访问临界资源的#xff0c;而我们访问临界资源的那部分代码称为临界区。
实现一个抢票系统
只有一个线程抢票时
#include ios…一、临界资源与临界区
多线程会共享例如全局变量等资源我们把会被多个执行流访问的资源称为临界资源我们是通过代码访问临界资源的而我们访问临界资源的那部分代码称为临界区。
实现一个抢票系统
只有一个线程抢票时
#include iostream
#include vector
#include unistd.h#include Thread.hppint num 10000; std::string GetThreadName()
{static int num 1;char name[64];snprintf(name, sizeof(name), thread-%d, num);return name;
}void Ticket(std::string name)
{while(true){if(num 0){usleep(1000);printf(%s get ticket: %d\n, name.c_str(), num);num--;}else{break;}}
}int main()
{std::string name1 GetThreadName();Threadstd::string t1(name1, Ticket, name1);t1.Start();t1.Join();return 0;
}
正常输出最终票数为0时退出。 但是当我们启动多个线程同时抢票时num就是临界资源使用num的那部分代码就是临界区
#include iostream
#include vector
#include unistd.h#include Thread.hppint num 10000; std::string GetThreadName()
{static int num 1;char name[64];snprintf(name, sizeof(name), thread-%d, num);return name;
}void Ticket(std::string name)
{while(true){if(num 0){usleep(1000);printf(%s get ticket: %d\n, name.c_str(), num);num--;}else{break;}}
}int main()
{std::string name1 GetThreadName();Threadstd::string t1(name1, Ticket, name1);std::string name2 GetThreadName();Threadstd::string t2(name2, Ticket, name2);std::string name3 GetThreadName();Threadstd::string t3(name3, Ticket, name3);std::string name4 GetThreadName();Threadstd::string t4(name4, Ticket, name4);t1.Start();t2.Start();t3.Start();t4.Start();t1.Join();t2.Join();t3.Join();t4.Join();return 0;
}
可以看到出现了0和负数的票数这是因为当票数只剩1时有多个执行流在同一时间通过了if判断使得能继续进行减票操作。 vs下自减操作的反汇编分为三步先从内存拿数据再把数据减1最后把数据拷贝到内存 多个执行流同时访问临界资源例如自减操作由于--操作不是原子性的我们认为一条汇编指令是原子性的是不会被中断的。但--操作转为汇编指令后需要多条指令才能完成当--操作执行到一半切换到其他线程会导致数据不一致的问题。这种情况下需要通过锁把临界区保护起来每次只让一个执行流访问临界资源避免数据不一致问题。
互斥任何时刻互斥保证有且只有一个执行流进入临界区访问临界资源通常对临界资源起保护作用。
原子性不会被任何调度机制打断的操作该操作只有两态要么完成要么未完成。 二、使用锁的方法
1.创建锁
如果定义一个全局的锁直接使用pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER用宏初始化。
如果定义一个局部锁要使用pthread_mutex_init方法创建参数attr设为nullptr 2.加锁解锁
使用pthread_mutex_lock加锁传递锁的地址
解锁用pthread_mutex_unlock
当我们使用锁后就能保证每次只有一个执行流能访问临界资源。
#include iostream
#include vector
#include unistd.h#include Thread.hppint num 10000;
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; //定义一个全局锁std::string GetThreadName()
{static int num 1;char name[64];snprintf(name, sizeof(name), thread-%d, num);return name;
}void Ticket(std::string name)
{while(true){pthread_mutex_lock(mutex); //加锁if(num 0){usleep(1000);printf(%s get ticket: %d\n, name.c_str(), num);num--;pthread_mutex_unlock(mutex); //解锁}else{pthread_mutex_unlock(mutex); //解锁break;}}
}int main()
{std::string name1 GetThreadName();Threadstd::string t1(name1, Ticket, name1);std::string name2 GetThreadName();Threadstd::string t2(name2, Ticket, name2);std::string name3 GetThreadName();Threadstd::string t3(name3, Ticket, name3);std::string name4 GetThreadName();Threadstd::string t4(name4, Ticket, name4);t1.Start();t2.Start();t3.Start();t4.Start();t1.Join();t2.Join();t3.Join();t4.Join();return 0;
}
结果正常但是速度慢了很多因为要不断申请锁和释放锁
加锁解锁的过程是安全的 三、可重入和线程安全
1.概念
线程安全多个线程并发同一段代码时不会出现不同的结果。常见对全局变量或者静态变量进行操作 并且没有锁保护的情况下会出现该问题。
重入同一个函数被不同的执行流调用当前一个流程还没有执行完就有其他的执行流再次进入我们称之为重入。一个函数在重入的情况下运行结果不会出现任何不同或者任何问题则该函数被称为可重入函数否则是不可重入函数。
2.常见的线程不安全的情况
1.不保护共享变量的函数
2.函数状态随着被调用状态发生变化的函数
3.返回指向静态变量指针的函数
4.调用线程不安全函数的函数
3.常见的线程安全的情况
1.调用了malloc/free函数因为malloc函数是用全局链表来管理堆的
2.调用了标准I/O库函数标准I/O库的很多实现都以不可重入的方式使用全局数据结构
3.可重入函数体内使用了静态的数据结构
4.常见可重入的情况
1.不使用全局变量或静态变量
2.不使用用malloc或者new开辟出的空间
3.不调用不可重入函数不返回静态或全局数据所有数据都有函数的调用者提供
4.使用本地数据或者通过制作全局数据的本地拷贝来保护全局数据
5.可重入与线程安全联系
1.函数是可重入的那就是线程安全的
2.函数是不可重入的那就不能由多个线程使用有可能引发线程安全问题
3.如果一个函数中有全局变量那么这个函数既不是线程安全也不是可重入的。
6.可重入与线程安全区别
1.可重入函数是线程安全函数的一种
2.线程安全不一定是可重入的而可重入函数则一定是线程安全的。
3.如果将对临界资源的访问加上锁则这个函数是线程安全的但如果这个重入函数若锁还未释放则会产生 死锁因此是不可重入的。
四、死锁
死锁是指在一组进程中的各个进程均占有不会释放的资源但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。
1.死锁四个必要条件
1.互斥条件一个资源每次只能被一个执行流使用
2.请求与保持条件一个执行流因请求资源而阻塞时对已获得的资源保持不放
3.不剥夺条件: 一个执行流已获得的资源在末使用完之前不能强行剥夺
4.循环等待条件: 若干执行流之间形成一种头尾相接的循环等待资源的关系
2.避免死锁
1.破坏死锁的四个必要条件
2.加锁顺序一致
3.避免锁未释放的场景
4.资源一次性分配
3.避免死锁算法
1.死锁检测算法
2.银行家算法
一个锁会造成死锁吗
答案是会的当一个线程申请完一个锁访问完临界资源后接下来该释放锁了但是代码却写成了加锁这就会导致死锁问题。