当前位置: 首页 > news >正文

公司网站模板图片电子商务实网站的建设

公司网站模板图片,电子商务实网站的建设,怎么做精准引流推广,关键词排名软件官网文章目录前言一、线程竞争案例二、互斥,锁三,线程安全3-1 为什么会线程不安全四,重入函数4-1 两种重入场景的具体分析五,死锁5-1 死锁的核心概念5-2 死锁产生的四大必要条件5-3 死锁的危害5-4 避免死锁的常用方法前言 学线程互斥…

文章目录

  • 前言
  • 一、线程竞争案例
  • 二、互斥,锁
  • 三,线程安全
    • 3-1 为什么会线程不安全
  • 四,重入函数
    • 4-1 两种重入场景的具体分析
  • 五,死锁
    • 5-1 死锁的核心概念
    • 5-2 死锁产生的四大必要条件
    • 5-3 死锁的危害
    • 5-4 避免死锁的常用方法


前言

学线程互斥的用处在于:在多线程程序里,很多数据和资源是共享的(比如全局变量、文件、socket、内存池)。如果不加限制,多个线程可能会在同一时间修改同一份数据,导致结果错误或者程序崩溃。互斥锁的作用就是保证某一段代码在同一时刻只能被一个线程执行,从而避免数据竞争,让结果稳定可靠。


一、线程竞争案例

我们来编写一个线程竞争资源的代码:

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
using namespace std;class customer {
public:int _ticket_num = 0;   // 该顾客买到的票数pthread_t _tid;        // 线程IDstring _name;          // 顾客名字
};int g_ticket = 10000;      // 总票数void* buyTicket(void* args) {customer* cust = (customer*)args;while (true) {if (g_ticket > 0) {usleep(1000);  // 模拟出票耗时cout << cust->_name << " get ticket: " << g_ticket << endl;g_ticket--;cust->_ticket_num++;} else {break;}}return nullptr;
}int main() {vector<customer> custs(5);// 创建 5 个顾客线程for (int i = 0; i < 5; i++) {custs[i]._name = "customer-" + to_string(i + 1);pthread_create(&custs[i]._tid, nullptr, buyTicket, &custs[i]);}// 等待所有顾客线程结束for (int i = 0; i < 5; i++) {pthread_join(custs[i]._tid, nullptr);}// 打印每个顾客买到的票数for (int i = 0; i < 5; i++) {cout << custs[i]._name << " get tickets: " << custs[i]._ticket_num << endl;}return 0;
}

我们先从自定义函数和自定义类开始讲解,再到main函数的讲解:

class customer:有三个参数,都是顾客自身的信息,在整体程序当中,每一个顾客对应一个线程

void* buyTicket:用于给顾客售票,顾客每增加一张票,则g_ticket减1

main:创建五个进程,并且相继竞争购票

演示结果:

gch@hcss-ecs-f59a:/gch/code/HaoHao/learn2/day6$ ./exe
......
customer-3 get ticket: 0
customer-4 get ticket: -1
customer-1 get ticket: -2
customer-5 get ticket: -3
customer-1 get tickets: 2000
customer-2 get tickets: 2001
customer-3 get tickets: 2000
customer-4 get tickets: 2001
customer-5 get tickets: 2002

那么这里就有问题了,明明只有1000张票,为什么五个人加起来的数量是1004张呢

  • if 语句判断条件为真以后,代码可以并发的切换到其他线程
  • --ticket 操作本身就不是一个原子操作

这个ticket在线程当中属于共享资源,因为只有一个CUP并且它是单核的,一次只能执行一个进程,为了实现进程的同步,操作系统内核会在线程还没执行完函数时打断线程,让CUP运行其它的线程,但是上一个线程还没执行玩ticket,下一个进程就已经进入if函数了,所以会导致--ticket被运行多次

  • g_ticket 是共享资源,因为所有线程都可以访问和修改它。多个线程同时访问它,就有可能出现冲突。

  • 虽然单核 CPU 在 同一时刻 只能执行一个线程,但操作系统会通过 时间片轮转 或 线程调度 不断切换线程。

    • 一个线程可能执行到一半(比如刚判断 g_ticket > 0)就被操作系统挂起。
    • CPU 会切换给另一个线程去执行。

假设线程 A 执行到:

if (g_ticket > 0) // 假设 g_ticket = 1

此时线程 A 还没来得及执行 g_ticket--。操作系统把 CPU 切给线程 B。线程 B 也执行到同样的 if (g_ticket > 0) 判断,此时它看到 g_ticket 还是 1,于是也进入 if。最终,两个线程都执行了 g_ticket--,但是 g_ticket 只应该被减一次。这就是 竞态条件:多个线程同时操作共享资源,导致结果错误


二、互斥,锁

要解决上述共享资源冲突问题,需要满足三点条件:

  • 互斥执行:当某个线程进入临界区执行代码时,其他线程不能同时进入该临界区。
  • 公平进入:如果多个线程同时请求进入临界区,而此时没有线程在执行,只允许其中一个线程进入。
  • 非阻塞退出:线程在临界区外时,不得阻止其他线程进入临界区

要做到这三点,本质上就是需要一把锁。Linux上提供的这把锁叫互斥量
在这里插入图片描述


演示代码:

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>
#include <cstring>
using namespace std;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
class customer
{
public:int _ticket_num = 0;pthread_t _tid;string _name;
};int g_ticket = 10000;void* buyTicket(void* args)
{customer* cust = (customer*)args;while(true){pthread_mutex_lock(&mutex);if(g_ticket > 0){usleep(1000);cout << cust->_name << " get ticket: " << g_ticket << endl;g_ticket--;cust->_ticket_num++;pthread_mutex_unlock(&mutex);}else{pthread_mutex_unlock(&mutex);break;}}return nullptr;
}int main()
{vector<customer> custs(5);for(int i = 0; i < 5; i++){custs[i]._name= "customer-" + to_string(i + 1);pthread_create(&custs[i]._tid, nullptr, buyTicket, &custs[i]);}for(int i = 0; i < 5; i++){pthread_join(custs[i]._tid, nullptr);}for(int i = 0; i < 5; i++){cout << custs[i]._name << " get tickets: " << custs[i]._ticket_num << endl;}return 0;
}

在这段代码里:

  • pthread_mutex_lock(&mutex) 让线程尝试获取锁,如果别的线程已经持有锁,它就会阻塞等待。
  • 当线程获得锁后,它才能进入临界区操作共享资源 g_ticket
  • 执行完之后,调用 pthread_mutex_unlock(&mutex) 释放锁,这样别的线程才能继续进入临界区。

所以,锁的作用就是:

  • 保证同一时刻只有一个线程能修改 g_ticket,避免出现多个线程同时减票而导致的数据错误。

演示结果:

gch@hcss-ecs-f59a:/gch/code/HaoHao/learn2/day6$ ./exe
......
customer-1 get tickets: 2214
customer-2 get tickets: 2391
customer-3 get tickets: 1761
customer-4 get tickets: 1806
customer-5 get tickets: 1828

上述是全局静态锁,如果是局部锁需要在使用完之后用pthread_mutex_destroy进行销毁
互斥锁初始化方式主要有三种

  • 静态初始化(全局/静态作用域)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  • 在 编译期 就完成初始化。

  • 适合全局变量、静态变量。

  • 生命周期随进程结束自动回收,不需要显式销毁

  • 动态初始化

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_destroy(&mutex);
  • 在 运行时 用 pthread_mutex_init 初始化。
  • 适合函数内的局部变量,或需要 动态分配的结构体里的成员。
  • 用完必须 pthread_mutex_destroy,否则可能导致资源泄漏。

三,线程安全

线程安全指的是:当多个线程同时访问某个函数、数据结构或代码片段时,程序的行为依然是正确的、可预期的,不会出现数据错乱或未定义行为

换句话说:

  • 线程安全:多个线程并发访问 → 程序结果仍然正确。
  • 线程不安全:多个线程并发访问 → 可能导致错误(数据竞争、死锁、崩溃)。

3-1 为什么会线程不安全

根本原因是:多线程共享内存,但调度是抢占式的。
假设我们有一个全局变量:

int counter = 0;void *worker(void *arg) {for (int i = 0; i < 10000; i++) {counter++;  // 不是原子操作!}return NULL;
}

如果两个线程同时执行 counter++:

实际会分解成三步:读 countercounter + 1写回 counter
假如两个线程交叉执行,就可能丢失更新(最终结果小于 20000)。
这就是 数据竞争,典型的线程不安全,所以我们在这种线程不安全的情况,我们可以使用锁来实现线程安全。
线程安全 = 在多线程并发情况下,程序逻辑和数据结果依旧正确,不会出现竞态问题。


四,重入函数

重入的核心是同一个函数被多个执行流交错执行。想象一个函数正在执行到一半,突然被打断(可能是另一个线程开始执行,或者信号处理函数触发),而这个打断它的执行流也调用了同一个函数,这就发生了重入


4-1 两种重入场景的具体分析

  1. 多线程重入(并发重入)
    当多个线程同时调用同一个函数时,就可能发生重入

可重入示例

// 可重入函数:只使用局部变量
int add(int a, int b) {int temp;  // 局部变量,每个线程有独立副本temp = a + b;return temp;
}

每个线程调用add()时,局部变量temp是线程私有,不会互相干扰。

不可重入示例

// 不可重入函数:使用全局变量
int global_num = 0;
int increment() {global_num++;  // 读取-修改-写入三步操作,可能被打断return global_num;
}

  1. 信号导致的重入(异步重入)
    当程序正在执行函数 A 时,突然收到信号,系统会暂停当前执行流,转去执行信号处理函数。如果信号处理函数也调用了函数 A,就会发生重入。

危险示例

#include <signal.h>
#include <stdio.h>FILE *file;void signal_handler(int signum) {// 信号处理函数也操作file,导致重入fputs("Signal handled\n", file);
}int main() {file = fopen("test.txt", "w");signal(SIGINT, signal_handler);  // 注册Ctrl+C的处理函数// 主程序正在操作file时,若收到信号会导致重入fputs("Main writing\n", file);// ... 其他操作fclose(file);return 0;
}
  • 主程序正在执行fputs()(操作全局变量file)时,若按下 Ctrl+C 触发信号
  • 信号处理函数也调用fputs()操作同一个file
  • 可能导致文件缓冲区数据混乱,甚至程序崩溃

3、可重入函数的判定准则
一个函数要成为可重入函数,必须满足:

  • 不使用全局变量或静态变量,或对其访问进行特殊保护
  • 不使用 malloc/free(会操作全局内存管理结构)
  • 不调用其他不可重入函数(如标准库中的printffputsI/O函数)
  • 不依赖硬件资源的状态(如不直接操作硬件寄存器)

五,死锁

死锁是并发编程中一种常见且危险的状态,指两个或多个执行流(线程、进程)相互等待对方持有的资源,且彼此都无法继续推进的僵局


5-1 死锁的核心概念

当多个执行执行流同时竞争有限的共享资源时,若每个执行流都持有一部分资源,同时又等待其他执行流释放所需资源,就会形成循环等待,导致所有执行流都无法继续执行,这种状态称为死锁。


5-2 死锁产生的四大必要条件

  • 互斥条件:资源具有排他性,同一时间只能被一个执行流使用(如一把锁只能被一个线程持有)。
  • 持有并等待条件:执行流已经持有至少一个资源,同时又在等待获取其他执行流持有的资源。
  • 不可剥夺条件:已获取的资源不能被强制剥夺,只能由持有者主动释放(如线程持有的锁不能被其他线程强制释放)。
  • 循环等待条件:存在执行流的循环链,每个执行流都在等待下一个执行流持有的资源(如线程 A 等线程 B 的资源,线程 B 等线程 A 的资源)。

演示代码:

#include <pthread.h>
#include <stdio.h>// 定义两个全局锁(资源)
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;// 线程1的执行函数:先锁lock1,再等lock2
void *thread1(void *arg) {pthread_mutex_lock(&lock1);printf("线程1持有lock1,等待lock2...\n");// 模拟处理时间,增加死锁概率sleep(1);pthread_mutex_lock(&lock2);  // 等待线程2释放lock2// 业务操作(实际中不会执行到)pthread_mutex_unlock(&lock2);pthread_mutex_unlock(&lock1);return NULL;
}// 线程2的执行函数:先锁lock2,再等lock1
void *thread2(void *arg) {pthread_mutex_lock(&lock2);printf("线程2持有lock2,等待lock1...\n");// 模拟处理时间,增加死锁概率sleep(1);pthread_mutex_lock(&lock1);  // 等待线程1释放lock1// 业务操作(实际中不会执行到)pthread_mutex_unlock(&lock1);pthread_mutex_unlock(&lock2);return NULL;
}int main() {pthread_t t1, t2;pthread_create(&t1, NULL, thread1, NULL);pthread_create(&t2, NULL, thread2, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);return 0;
}

执行结果:

线程 1 持有 lock1 并等待 lock2线程 2 持有 lock2 并等待 lock1,形成循环等待,程序永远卡在等待状态,即死锁。


5-3 死锁的危害

  • 程序卡住,无法继续执行,需要强制终止
  • 资源被永久占用,无法释放
  • 难以调试,死锁可能在特定 timing 下才触发,复现困难

5-4 避免死锁的常用方法

  • 破坏循环等待条件:对所有资源按固定顺序获取(如规定必须先获取 lock1,再获取 lock2)。
    破坏持有并等待条件:一次性获取所有所需资源,获取不到则释放已持有的资源并重试。
    使用带超时的锁:如pthread_mutex_timedlock,超时后释放资源并重新尝试。
    定期检测死锁:通过工具(如pstackgdb)或自定义算法检测死锁,发现后强制释放资源。
http://www.dtcms.com/a/571389.html

相关文章:

  • 重庆电商网站建设费用网络规划设计师视频教程下载
  • 风景区网站代码网站百度收录变少
  • Postman 的汉化安装中文版及使用指南!
  • 做网站闵行2017 WordPress 主题
  • 网站建设找谁做网站制作公司网站建设公司
  • 网站建设思路方向淘宝官网网页版登录入口
  • 沈阳城市建设管理学校网站群晖WordPress绑定域名
  • 网站怎么发布信息商丘seo快速排名
  • 惠州专业网站设计公司移动端ui
  • 电子商务网站建设方式建设执业资格注册中心网站
  • 彬县网站建设深圳网站搭建电话
  • 衡水网站设计哪家专业外贸网站怎么做促销
  • 电子商务平台如何推广营销抖音seo搜索优化
  • mc做地图画网站wordpress 提交
  • 网站建设需要保存什么网站页尾设计
  • 《网站平台建设》课程实训wordpress社团网站
  • 开平建设局网站网站重新建设的通知
  • 买一个网站需要多少钱哪个网站学习做辅助
  • 廊坊市安次区建设局网站商业规划设计公司
  • 海珠营销型网站建设上海火迎网络推广运营优化
  • 已经有域名,如何建设网站贺贵江seo教程
  • 南昌网站推广公司查权重工具
  • 网站建设系统平台rtt全民互助平台网站开发
  • 中国工程建设造价信息网站网站开发公司 网站空间
  • 网站字体怎么修改seo网络营销技巧
  • 自己做网站可以揽业务吗温州网站设计制作课程
  • 站长之家ppt素材做网站,用什么做数据库最好
  • jf厂高仿手表网站开发游戏用什么编程软件
  • 帮传销做网站违法吗做设计的素材网站
  • 浙江住房与城乡建设部网站网站页面做