创建进程,设计信号量同步机制,实现多线程同步 - C语言版

news/2024/10/9 0:32:27/文章来源:https://www.cnblogs.com/chenheading/p/18453324
  • 环境:Windows11
  • 编译器:Visual Studio 2019

相关头文件:

#include <windows.h>
#include <stdio.h>

相关函数:

  1. 睡眠等待函数:Sleep(int millisecond);
    睡眠等待一定时间,会造成OS重新调度其它的线程运行
Sleep(10);   //当前线程睡眠10毫秒后重新执行
  1. 创建进程
CreateProcess(LPSECURITY_ATTRIBUTES // 是否继承进程句柄LPSECURITY_ATTRIBUTES //是否继承线程句柄BOOL bInheritHandles //是否继承句柄DWORD dwCreationFlags //有没有创建标志LPVOID lpEnvironment // 是否使用父进程环境变量LPCTSTR lpCurrentDirectory //使用父进程目录作为当前目录,可以自己设置目录LPSTARTUPINFO lpStartupInfo //STARTUPINFOW结构体详细信息(启动状态相关信息)LPPROCESS_INFORMATION //PROCESS_INFORMATION结构体进程信息
);
  1. 启动线程:CreateThread(ThreadAttribures, stack_size, ThreadFunctionAddress, Parameters, CreationFlags, ThreadID);
HANDLE t1 = CreateThread(NULL,0,Func,NULL,0,&ThreadID);
  1. 定义信号量:Semaphore
HANDLE sema;
  1. 创建信号量:CreateSemaphore(Attributes,InitialCount, MaxCount, SemaphoreID);
sema = CreateSemaphore(NULL, 0, 1, NULL);
  1. 申请访问信号量:WaitForSingleObject(HANDLE, millisecond);
    调用该函数后,如果信号量为0,则线程阻塞;否则信号量当前值减1,然后继续执行下一行语句;
WaitForSingleObject(sema,INFINITE);
  1. 释放信号量:ReleaseSemaphore(HANDLE, releaseCount, *PreviousCount);
    调用该函数后,信号量当前值加上releastCount,然后继续执行下一行语句;
ReleaseSemaphore(sema,1,NULL);    //信号量加1

--- 1. 在程序中根据用户输入的可执行程序名称,创建一个进程来运行该可执行程序。 ```c #define _CRT_SECURE_NO_WARNINGS #include #include

int main() {
// 定义和初始化STARTUPINFO 和 PROCESS_INFROMATION结构体
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si)); // 将 si 结构体的内存置零,确保其中没有任何残留数据
si.cb = sizeof(si); // 表示结构体的大小
ZeroMemory(&pi, sizeof(pi));

char name[100]; // 接收文件名
char path[100]; // 完整路径
wchar_t wpath[200]; // 宽字符路径printf("Please input program to run: ");
scanf("%99s", name);// 将基础路径拷贝到 path 中
strcpy(path, "C:\\Windows\\System32\\");// 拼接文件名到路径
strcat(path, name);mbstowcs(wpath, path, sizeof(wpath) / sizeof(wchar_t));// 创建进程
if (!CreateProcess(wpath,  // 文件路径NULL, // 命令行参数NULL, // 默认安全属性NULL, // 默认安全属性FALSE, // 不继承句柄0, // 默认创建标志NULL, // 使用父进程的环境变量NULL, // 使用父进程的当前目录&si, // 启动信息&pi) // 进程信息) {printf("创建进程失败\n");return -1;
}// 等待进程结束
WaitForSingleObject(pi.hProcess, INFINITE);
// 关闭进程和线程句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);return 0;

}

<br>
> **个人问题记录:**起初测试CreateProcess函数是否正确传参时发现该函数不兼容char *类型
![](https://img2024.cnblogs.com/blog/3531058/202410/3531058-20241009001909402-1354116522.png)- 在 Windows API 中,使用 Unicode 字符串时,需要使用 L 前缀来定义宽字符字符串:
![](https://img2024.cnblogs.com/blog/3531058/202410/3531058-20241009001934208-364686058.png)
<br>
2. 假设有四个线程,第一个线程输出字符串 “This”,第二个线程输出字符串 “is”, 第三个线程输出字符串“Jinan”, 第四个线程输出字符串 “University!”。编制C/C++程序,在主程序main函数中创建四个线程并依次启动,设计信号量(Semaphore)同步机制,当主程序运行时,屏幕输出的结果是字符串“This is Jinan University!” 
```c
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>HANDLE sema;  // 定义信号量
const char* words[] = { "This", "is", "Jinan", "University!" };DWORD WINAPI ThreadFunction(LPVOID lpParam) {// 申请访问信号量WaitForSingleObject(sema, INFINITE);int threadNum = *((int*)lpParam);printf("%s ", words[threadNum - 1]);// 释放信号量ReleaseSemaphore(sema, 1, NULL);return 0;
}
int main() {HANDLE threads[4]; // 存储线程句柄的数组DWORD threadID;// 创建信号量,初始计数为0,最大计数为1sema = CreateSemaphore(NULL, 0, 1, NULL);if (sema == NULL) {printf("创建信号量失败\n");return 1;}// 创建并启动线程for (int i = 0; i < 4; i++) {int* pNum = (int*)malloc(sizeof(int)); // 为每个线程分配内存*pNum = i + 1; // 设置线程编号threads[i] = CreateThread(NULL,               // 默认安全属性0,                  // 默认堆栈大小ThreadFunction,     // 线程函数的地址pNum,                 // 传递线程编号0,                  // 默认创建标志&threadID           // 线程标识符);if (threads[i] == NULL) {printf("线程 %d 启动失败\n", i + 1);return 1;}// 释放信号量Sleep(100);ReleaseSemaphore(sema, 1, NULL);}// 等待所有线程完成WaitForMultipleObjects(4, threads, TRUE, INFINITE);// 关闭线程句柄for (int i = 0; i < 4; i++) {CloseHandle(threads[i]);}// 关闭信号量句柄CloseHandle(sema);return 0;
}

个人问题记录:

输出threadNum时,发现for循环里每个输出的threadNum都是4

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>HANDLE sema;  // 定义信号量DWORD WINAPI ThreadFunction(LPVOID lpParam) {// 申请访问信号量WaitForSingleObject(sema, INFINITE);int threadNum = *((int*)lpParam);printf("%d\n", threadNum);// 释放信号量ReleaseSemaphore(sema, 1, NULL);return 0;
}
int main() {HANDLE threads[4]; // 存储线程句柄的数组DWORD threadID;// 创建信号量,初始计数为0,最大计数为1sema = CreateSemaphore(NULL, 0, 1, NULL);if (sema == NULL) {printf("创建信号量失败\n");return 1;}// 创建并启动线程for (int i = 0; i < 4; i++) {threads[i] = CreateThread(NULL,               // 默认安全属性0,                  // 默认堆栈大小ThreadFunction,     // 线程函数的地址&i,                 // 传递线程编号0,                  // 默认创建标志&threadID           // 线程标识符);if (threads[i] == NULL) {printf("线程 %d 启动失败\n", i + 1);return 1;}// 释放信号量ReleaseSemaphore(sema, 1, NULL);}// 等待所有线程完成WaitForMultipleObjects(4, threads, TRUE, INFINITE);// 关闭线程句柄for (int i = 0; i < 4; i++) {CloseHandle(threads[i]);}// 关闭信号量句柄CloseHandle(sema);return 0;
}

  • 因为传递给线程函数的参数是变量 i 的地址,而在循环中 i 是递增的。所有线程都在共享同一个 i 的地址,所以当所有线程运行时,它们都看到的是 i 的最终值——即循环结束后 i 的值是 4
  • 应该为每个线程分配内存:int* pNum = (int*)malloc(sizeof(int));



  1. 基于实验题目2,在主函数中依次启动四个线程,修改主程序,使得给定用户任意输入的整数n,程序输出n个同样的字符串“This is Jinan University!”

在题目2的基础上套一层for循环即可

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>HANDLE sema;  // 定义信号量
const char* words[] = { "This", "is", "Jinan", "University!" };DWORD WINAPI ThreadFunction(LPVOID lpParam) {// 申请访问信号量WaitForSingleObject(sema, INFINITE);int threadNum = *((int*)lpParam);printf("%s ", words[threadNum - 1]);// 释放信号量ReleaseSemaphore(sema, 1, NULL);return 0;
}int main() {int n;printf("n = ");scanf("%d", &n);for (int k = 0; k < n; k++) {HANDLE threads[4]; // 存储线程句柄的数组DWORD threadID;// 创建信号量,初始计数为0,最大计数为1sema = CreateSemaphore(NULL, 0, 1, NULL);if (sema == NULL) {printf("创建信号量失败\n");return 1;}// 创建并启动线程for (int i = 0; i < 4; i++) {int* pNum = (int*)malloc(sizeof(int)); // 为每个线程分配内存*pNum = i + 1; // 设置线程编号threads[i] = CreateThread(NULL,               // 默认安全属性0,                  // 默认堆栈大小ThreadFunction,     // 线程函数的地址pNum,                 // 传递线程编号0,                  // 默认创建标志&threadID           // 线程标识符);if (threads[i] == NULL) {printf("线程 %d 启动失败\n", i + 1);return 1;}// 释放信号量Sleep(100);ReleaseSemaphore(sema, 1, NULL);}// 等待所有线程完成WaitForMultipleObjects(4, threads, TRUE, INFINITE);// 关闭线程句柄for (int i = 0; i < 4; i++) {CloseHandle(threads[i]);}// 关闭信号量句柄CloseHandle(sema);printf("\n");}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/810262.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

古典+ezRSA

​ 古典密码在线工具:https://ctf.bugku.com/tools.html 一键解码工具库:随波逐流,在github上下载即可 注:古典密码只需做个了解,因为很多都是靠工具实现的,多刷题有个印象,遇到题能看出像什么密码就好。 Base家族 在密码学领域,"base" 通常指的是一种编码方…

【专题】2024年母婴行业发展趋势洞察报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37818 在当今消费市场中,母婴领域正经历着深刻的变革。随着需求日益多元化,母婴用户的消费习惯和选购考量因素发生了显著变化。 分龄分层产品逐渐成为主流,这背后是用户对个性化解决方案的追求以及对孩子成长各方面的细致关注。同时,母婴…

Serilog文档翻译系列(七) - 应用设置、调试和诊断、开发接收器

Serilog支持通过App.config和Web.config中的01、应用设置 Serilog 支持在 App.config 和 Web.config 文件中使用简单的 配置语法,以设置最低日志级别、为事件添加额外属性以及控制日志输出。 Serilog 主要通过代码进行配置,设置支持旨在作为补充功能。虽然不是全面的,但大多…

【视频讲解】Python量子计算聚类Q-means:量子k-means算法分析电路数据实现可视化

全文链接:https://tecdat.cn/?p=37821 原文出处:拓端数据部落公众号 分析师:Yifan Zhang 量子计算在近期已然成为一个频繁出现的热门概念。尽管它在大众认知以及互联网社区中备受瞩目,热度极高,然而就其实际能力而言,当前仍然存在诸多局限。 量子计算作为一个全新的领域…

每日打卡

今天连接phoenix,版本是5.1.3-hbase-2.5,主要连接dbeaver,idea老是报错。

20222417 2024-2025-1 《网络与系统攻防技术》实验一实验报告

1.实验内容 (1).掌握反汇编与十六进制编程器 (2).能正确修改机器指令改变程序执行流程 (3).能正确构造payload进行bof攻击 2.实验过程 (1).直接修改程序机器指令,改变程序执行流程 将pwn1文件放入共享文件夹,后续在kali中使用,再将文件复制到实验文件夹share路径…

第一课 php基础语法 变量 函数

php语法<?php// 代码段   ?> php输出方法:echo 和 print不同点:echo-能够输出一个以上的字符串,英文逗号隔开print-只能输出一个字符串,并始终返回1echo 比 print 稍快,并且开销低 注释注释不会被作为程序来读取和执行。它唯一的作用是供代码编辑者阅读(让别人…

CentOS 8 停止维护后通过 rpm 包手动安装 docker

根据 Docker官方文档 的指引,进入 Docker rpm 包下载的地址,根据自己系统的架构和具体版本选择对应的路径这里我使用 https://download.docker.com/linux/centos/7/x86_64/stable 版本,根据 docker 官方的给出的安装命令选择性的下载对应的 rpm 包最终使用 yum 命令安装下载…

02 Vue默认项目说明

1. node_modules pnpm 安装的第三方依赖 2. public 公共资源,存放网页图标等 3. src 开发代码存放位置 3.1 项目入口文件 main.ts import { createApp } from vue // 引入vue import ./style.css // 引入默认样式 import App from ./App.vue // 引入页面 App.VuecreateApp(App…

解构UI设计

解构UI设计 第一章 界面类型 1.1 闪屏页 又称为启动页,就是APP启动在进入功能主界面前用户看到的页面。 闪屏页决定了用户对App的第一印象。 闪屏页显示的时间很短,通常只有1秒。 闪屏页分为品牌宣传型、节日关怀型和活动推广型3种类型。 1.1.1 品牌宣传型 App的闪屏页是为体…

创建新的 App 页面

完整的页面创建过程包括三个步骤:在 layout 目录下创建 XML 文件创建与 XML 文件对应的 Java 代码在 AndroidManifest.xml 中注册页面配置实现两个 Activity 相互跳转的代码: MainActivity: package com.example.myapplication1;import androidx.appcompat.app.AppCompatActiv…

[Vue] 异步路由组件和非路由组件的区别?

异步路由组件 都知道异步路由组件通过 () => import("./views/Home.vue") 导入路由组件。 但是它怎么就按需加载资源、代码分割了? 不同的代码解析报告 非异步路由组件异步路由组件