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

chenheading / 2024-10-09 / 原文

  • 环境: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;

}


个人问题记录:

输出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,最大计数为1
	sema = 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,最大计数为1
		sema = 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;
}