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

建设公司网站的好处学生网页制作成品

建设公司网站的好处,学生网页制作成品,网络架构图用什么软件画,阳逻开发区网站建设中企动力本篇旨在通过几个案例来学习父子进程的线程异步性 一、父进程与子进程 我们将要做的&#xff1a; 创建父子进程&#xff0c;观察父子进程执行的顺序&#xff0c;了解进程执行的异步行为 源代码&#xff1a; #include <stdio.h> #include <sys/types.h> #include…

本篇旨在通过几个案例来学习父子进程的线程异步性

一、父进程与子进程

我们将要做的: 创建父子进程,观察父子进程执行的顺序,了解进程执行的异步行为

源代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h> // 定义了 POSIX 操作系统 API(Unix/Linux 下的系统调用函数)
#include <stdlib.h>int main()
{pid_t pid; // 进程idchar*msg;  // 信息缓冲区int k;     // 变量,后面用于控制执行打印的次数printf("观察父子进程执行的先后顺序,了解调度算法的特征\n");pid=fork(); // 创建子进程switch(pid){case 0:msg="子进程在运行";k=3;break;case -1:msg="进程创建失败";break;    default:msg="父进程在运行";k=5;break;}while(k>0){puts(msg);	sleep(1);	k--;		}exit(0);
}

🔧1. 头文件讲解

  1. #include <sys/types.h>
  • 作用:定义数据类型,如 pid_t
  • pid_t 是一个整型,用于表示进程 ID,确保跨平台一致性。
  • 一般与 fork()wait() 等系统调用一起使用。
  1. #include <unistd.h>
  • 作用:定义了 POSIX 操作系统 API(Unix/Linux 下的系统调用函数)。
  • 提供本程序中用到的:
    • fork():创建子进程
    • sleep():让进程休眠若干秒
    • 还包括 getpid()(获取进程ID)、exec族函数(程序替换)等。
  1. #include <stdlib.h>
  • 作用:标准库函数,如内存分配、程序控制等。
  • 本程序中使用了:
    • exit(0):正常退出当前进程(0 表示正常退出)

🧠 2. 核心函数讲解

fork()

  • 函数原型:pid_t fork(void);
  • 作用:创建一个新的子进程,该子进程是调用它的进程的副本。
  • 返回值:
    • 父进程中fork() 返回子进程的 PID(大于 0)
    • 子进程中fork() 返回 0
    • 创建失败返回 -1

switch(pid)

  • 根据 fork() 的返回值来判断当前是:
    • 子进程pid == 0
    • 父进程pid > 0
    • 创建失败pid == -1

puts(msg)

  • 输出字符串 msg 并自动换行,功能类似于 printf("%s\\n", msg);,但更简单。

sleep(1)

  • 暂停当前线程执行 1 秒钟,模拟处理过程,也便于观察进程输出顺序。

exit(0)

  • 正常终止当前进程。系统看到返回值 0,认为程序成功执行。

📌 3. 程序运行逻辑总结

  1. 调用 fork() 创建子进程,得到两个并发执行的进程。
  2. 每个进程根据 fork() 的返回值设定自己的输出内容(msg)和输出次数(k)。
  3. 每个进程都进入 while(k>0) 循环,每秒输出一次 msg,共输出 k 次。
  4. 最终执行 exit(0) 正常退出。

🧪 4. 运行效果说明

实际运行时,输出类似:

观察父子进程执行的先后顺序,了解调度算法的特征
父进程在运行
子进程在运行
子进程在运行
父进程在运行
...

由于父子进程是并发执行的,它们输出的先后顺序会随着调度器算法系统负载等因素而变化。


二、主进程与子进程

我们将做的: 创建主线程和子线程,观察多线程执行的顺序,了解线程执行的异步行为

源代码:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h> // POSIX 线程库函数static int run=1;    // 子线程循环判断条件,主线程设置为 0 后子线程结束
static int retvalue; // 子线程退出时返回的值,供主线程获取void *threadfunc(void*arg)
{int*running=arg; // 接受主线程传入的参数printf("子线程初始化完毕,传入参数为:%d\n",*running);	while(*running)	//子线程通过 *running 控制循环是否继续{printf("子线程正在运行\n");usleep(1); // 微秒级休眠}printf("子线程退出\n");retvalue=8;pthread_exit((void*)&retvalue); // 返回 retvalue 的地址给主线程
}
int main()
{pthread_t tid; // 线程idint ret=-1;int times=3;int i=0;int *ret_join=NULL;// 创建一个线程,线程函数为threadfunc,传入参数为&runret=pthread_create(&tid,NULL,(void*)threadfunc,&run);	if(ret!=0){printf("建立线程失败\n");return 1;}printf("主线程创建子线程后在运行...\n");usleep(1);	// 主线程短暂休眠,为了让子线程有机会先运行printf("主线程调用usleep(1)...\n");for(;i<times;i++){printf("主线程打印i=%d\n",i);usleep(1);}run=0; // 子进程控制参数设置为0,通知子进程结束pthread_join(tid,(void*)&ret_join);	printf("线程返回值为:%d\n",*ret_join);return 0;
}

这段代码是一个使用 pthread 实现多线程的基础示例,展示了如何创建线程、线程间共享数据、线程退出返回值,以及主线程如何等待子线程完成。下面详细逐行讲解:


✅ 1. 头文件讲解

#include <pthread.h>     // POSIX 线程库函数
  • pthread.h 是 POSIX 标准线程库头文件,提供线程创建、同步、退出等函数定义。

🧠 2. 全局变量定义

static int run = 1;          // 子线程循环判断条件,主线程设置为 0 后子线程结束
static int retvalue;         // 子线程退出时返回的值,供主线程获取
  • run 是主线程与子线程共享的控制变量。
  • retvalue 将作为子线程 pthread_exit 返回值的地址,供主线程获取。

🚀 3. 线程函数 threadfunc

void *threadfunc(void* arg)
{int* running = arg;printf("子线程初始化完毕,传入参数为:%d\n", *running);	while (*running){printf("子线程正在运行\n");usleep(1); // 微秒级休眠(1 微秒 = 0.001 毫秒)}printf("子线程退出\n");retvalue = 8;pthread_exit((void*)&retvalue); // 返回 retvalue 的地址给主线程
}
✅ 关键点说明:
  • void *threadfunc(void* arg) 是 pthread 要求的线程函数格式。
  • arg 是传入的参数,实际是主线程传入 &run
  • 子线程通过 *running 控制循环是否继续。
  • 使用 pthread_exit() 显式结束线程,并返回结果指针。

🧵 4. 主线程 main

pthread_t tid; // 声明线程 id
int ret = -1;  // 初始化返回值
int times = 3; // 打印次数
int i = 0;
int *ret_join = NULL;
ret = pthread_create(&tid, NULL, (void*)threadfunc, &run);
  • 创建一个线程,线程函数为 threadfunc,传入参数为 &run
  • ret 为返回值,0 表示成功。
if(ret != 0)
{printf("建立线程失败\n");return 1;
}
printf("主线程创建子线程后在运行...\n");
usleep(1);	
printf("主线程调用usleep(1)...\n");
  • 主线程短暂停顿,为了让子线程有机会先运行。
for(; i < times; i++)
{printf("主线程打印i=%d\n", i);usleep(1);
}
  • 主线程执行 3 次循环,每次 sleep 1 微秒,并打印当前 i
run = 0;
  • 将共享变量 run 设置为 0,通知子线程退出。
pthread_join(tid, (void*)&ret_join);
  • 等待子线程结束,并获取返回值。
  • 注意这里 ret_joinint * 类型,用于接收 retvalue 的地址。
printf("线程返回值为:%d\n", *ret_join);
  • 打印子线程返回的值 8

🧪 5. 运行输出示例(大致)

主线程创建子线程后在运行...
主线程调用usleep(1)...
子线程初始化完毕,传入参数为:1
子线程正在运行
主线程打印i=0
子线程正在运行
主线程打印i=1
子线程正在运行
主线程打印i=2
子线程正在运行
子线程退出
线程返回值为:8

(线程调度不确定,输出顺序可能变化)


📌 6. 知识点总结

项目内容说明
pthread_create创建新线程
pthread_join等待线程结束,获取返回值
pthread_exit子线程退出并返回值
usleep(x)微秒级休眠,适合线程示例中短暂等待
共享变量通信主线程修改 run,控制子线程退出

⚠️ 7. 建议与优化

  1. usleep(1) 睡眠时间太短(1 微秒),可以改为 usleep(100000)(即 0.1 秒)方便观察输出。
  2. 变量 retvalue 设置为 static 是为了确保其生命周期足够长,返回地址有效。
  3. 多线程程序应考虑线程安全问题,如资源竞争、内存可见性等,在多核机器中尤为重要。

三、多线程对共享变量的非互斥访问

我们将要做的: 构造「多线程共享变量竞争」的现象,并分析现象发生的原因,进而思考解决方式。

源代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>int num=30,count=10;void *sub1(void *arg) {int i = 0,tmp;for (; i <count; i++){tmp=num-1;usleep(13);num=tmp;printf("线程1 num减1后值为: %d\n",num);}return ((void *)0);
}
void *sub2(void *arg){int i=0,tmp;for(;i<count;i++){tmp=num-1;usleep(31);num=tmp;printf("线程2 num减1后值为: %d\n",num);}return ((void *)0);
}
int main(int argc, char** argv) {pthread_t tid1,tid2; // 两个子线程的idint err,i=0,tmp;void *tret; // 线程返回值err=pthread_create(&tid1,NULL,sub1,NULL);if(err!=0){printf("pthread_create error:%s\n",strerror(err));exit(-1);}err=pthread_create(&tid2,NULL,sub2,NULL);if(err!=0){printf("pthread_create error:%s\n",strerror(err));exit(-1);}for(;i<count;i++){tmp=num-1;usleep(5);num=tmp;printf("main num减1后值为: %d\n",num);}printf("两个线程运行结束\n");err=pthread_join(tid1,&tret);if(err!=0){printf("can not join with thread1:%s\n",strerror(err));exit(-1);}printf("thread 1 exit code %d\n",(int)tret);err=pthread_join(tid2,&tret);if(err!=0){printf("can not join with thread1:%s\n",strerror(err));exit(-1);}printf("thread 2 exit code %d\n",(int)tret);return 0;
}

🧠 1. 程序功能概述

创建了两个线程 sub1sub2,以及主线程三者共同对一个全局变量 num 执行减 1 操作,共减去 count * 3 = 30 次。

初始值:

int num = 30, count = 10;

所以理论上最终 num == 0,但实际上并不一定!


⚠️ 2. 存在的核心问题:数据竞争(Race Condition)

❗ 对 num-- 是分三步执行的:
tmp = num - 1;
usleep(x);
num = tmp;

这个过程不是原子操作,多个线程可能“交叉”访问这个变量,造成竞态条件(Race Condition)

中间插入 usleep() 只是为了放大并发写入带来的冲突概率,模拟真实环境下的并发问题。

举例说明:

假设此时 num = 10,两个线程同时读到:

线程1:tmp1 = 10 - 1 = 9,睡眠
线程2:tmp2 = 10 - 1 = 9,睡眠

然后:

线程1醒来执行 num = 9
线程2醒来执行 num = 9 (覆盖了线程1的操作)

🔴 这样 num 实际只减少了一次,而我们期望它减少两次(一个线程分别减少一次)!


🔍 3. 运行效果举例(输出可能类似):

线程1 num减1后值为: 29
线程2 num减1后值为: 28
main num减1后值为: 27
线程1 num减1后值为: 27  ←❗ 重复了
main num减1后值为: 26
线程2 num减1后值为: 26  ←❗ 再次重复

最终 num 的值可能 不是 0,甚至是更高。原因就是上面说的:很多次减法操作失效了。


✅4. 如何解决?使用线程同步机制:互斥锁 pthread_mutex_t

例如,添加全局互斥锁

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

将每个对 num 的访问部分用锁保护:

pthread_mutex_lock(&lock);
tmp = num - 1;
usleep(13); // 保留你原来的模拟处理
num = tmp;
pthread_mutex_unlock(&lock);

🔒 这样确保每次只有一个线程在访问和修改 num


🛠️ 5. 修改后关键片段示例(以 sub1 为例)

void *sub1(void *arg) {int i = 0, tmp;for (; i < count; i++) {pthread_mutex_lock(&lock);tmp = num - 1;usleep(13);num = tmp;printf("线程1 num减1后值为: %d\n", num);pthread_mutex_unlock(&lock);}return ((void *)0);
}

主线程、sub2 中也要加锁。


🔚 6. 总结

问题说明
数据竞争多线程访问全局变量未加锁
后果num 最终值不确定,减法丢失
解决使用 pthread_mutex 互斥锁
调试建议加 -fsanitize=thread 或使用 valgrind --tool=helgrind 检查
http://www.dtcms.com/wzjs/501598.html

相关文章:

  • 深圳网站设计机构百度分析工具
  • 深圳松岗网站建设域名停靠网页app推广大全
  • 域名历史记录查询网站福州网站建设策划
  • 网站安全访问什么是seo教程
  • 桐乡网站设计公司百度医生
  • wordpress集成qq登录seo怎么收费的
  • 品牌营销咨询公司是做什么的青岛seo网络优化公司
  • 企业网站建设立项报告有人看片吗免费观看视频
  • 网站改域名备案吗宝鸡百度seo
  • 谷歌官方网站注册济南谷歌推广
  • 提高网站排名企业网站关键词优化
  • 企业网站维护工作计划企业宣传推广怎么做
  • 一个人做网站郑州见效果付费优化公司
  • 济南品牌营销型网站建设网页优化seo公司
  • 一台云服务器做多个网站游戏行业seo整站优化
  • 什么网站可以做图片知了seo
  • 河北汉佳 做网站的公司今日国内新闻最新消息大事
  • 给网站做网络安全的报价长沙网络营销外包哪家好
  • 武进区城乡建设局网站高质量软文
  • 泉州网上办事大厅济南优化网站关键词
  • 网站资源建设的步骤如何去除痘痘有效果
  • 做电子书网站 赚钱seo关键词排名优化教程
  • 最近的战争新闻大事江苏网站seo
  • 地方网站推广网站推广app软件
  • 大尺度做爰后入网站网站seo优化方案策划书
  • 网站设计与制作简单吗品牌推广内容
  • 做网站 找风投seo提高关键词
  • 做高防鞋 哪个网站能上架新闻头条今日最新消息
  • 网站的分页效果怎么做火星培训机构收费明细
  • 云南电商网站建设重庆seo主管