- 环境:Windows11
- 编译器:Visual Studio 2019
相关头文件:
#include <windows.h>
#include <stdio.h>
相关函数:
- 睡眠等待函数:
Sleep(int millisecond);
睡眠等待一定时间,会造成OS重新调度其它的线程运行
Sleep(10); //当前线程睡眠10毫秒后重新执行
- 创建进程
CreateProcess(LPSECURITY_ATTRIBUTES // 是否继承进程句柄LPSECURITY_ATTRIBUTES //是否继承线程句柄BOOL bInheritHandles //是否继承句柄DWORD dwCreationFlags //有没有创建标志LPVOID lpEnvironment // 是否使用父进程环境变量LPCTSTR lpCurrentDirectory //使用父进程目录作为当前目录,可以自己设置目录LPSTARTUPINFO lpStartupInfo //STARTUPINFOW结构体详细信息(启动状态相关信息)LPPROCESS_INFORMATION //PROCESS_INFORMATION结构体进程信息
);
- 启动线程:
CreateThread(ThreadAttribures, stack_size, ThreadFunctionAddress, Parameters, CreationFlags, ThreadID);
HANDLE t1 = CreateThread(NULL,0,Func,NULL,0,&ThreadID);
- 定义信号量:
Semaphore
HANDLE sema;
- 创建信号量:
CreateSemaphore(Attributes,InitialCount, MaxCount, SemaphoreID);
sema = CreateSemaphore(NULL, 0, 1, NULL);
- 申请访问信号量:
WaitForSingleObject(HANDLE, millisecond);
调用该函数后,如果信号量为0,则线程阻塞;否则信号量当前值减1,然后继续执行下一行语句;
WaitForSingleObject(sema,INFINITE);
- 释放信号量:
ReleaseSemaphore(HANDLE, releaseCount, *PreviousCount);
调用该函数后,信号量当前值加上releastCount,然后继续执行下一行语句;
ReleaseSemaphore(sema,1,NULL); //信号量加1
--- 1. 在程序中根据用户输入的可执行程序名称,创建一个进程来运行该可执行程序。 ```c #define _CRT_SECURE_NO_WARNINGS #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));
- 基于实验题目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;
}