Creating Threads - 创建线程

本文介绍了使用CreateThread函数在进程中创建新线程的方法,并详细解释了该函数的参数及用途。通过示例代码展示了如何定义线程函数、创建线程、等待线程结束以及释放资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The CreateThread function creates a new thread for a process. The creating thread must specify the starting address of the code that the new thread is to execute. Typically, the starting address is the name of a function defined in the program code (for more information, see ThreadProc). This function takes a single parameter and returns a DWORD value. A process can have multiple threads simultaneously executing the same function.

CreateThread 函数作用是在进程中创建一个新的线程. 创建的线程必须指定线程执行的起始地址. 特别地, 这个起始地址是在程序代码中定义的函数名(可以在ThreadProc中查看更多信息).此函数接受一个参数,返回值是一个DWORD类型的值. 一个进程可以有多个线程,且多个线程可以同时执行同一个函数.

The following is a simple example that demonstrates how to create a new thread that executes the locally defined function, MyThreadFunction.

下面一个简单的例子,展示了如何创建一个线程,并加载执行一个函数(MyThreadFunction).

The calling thread uses the WaitForMultipleObjects function to persist until all worker threads have terminated. The calling thread blocks while it is waiting; to continue processing, a calling thread would use WaitForSingleObject and wait for each worker thread to signal its wait object. Note that if you were to close the handle to a worker thread before it terminated, this does not terminate the worker thread. However, the handle will be unavailable for use in subsequent function calls.

主调线程中使用WaitForMultipleObjects函数来实现当前线程的持续活动,直到其他工作的线程消亡. 等待时,主调线程处理阻塞等待状态.为使进程继续,主调线程会使用WaitForSingleObject 并等待其他 工作中的线程来标记它等待的对象. 如果要在一个工作的线程消亡前关闭其句柄,达不到关闭线程的目的.但是,这个线程将会成为一个野线程,后续的其他函数就无法再使用它了.



可执行代码

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>

#define MAX_THREADS 3
#define BUF_SIZE 255

DWORD WINAPI MyThreadFunction(LPVOID lpParam);
void ErrorHandler(LPTSTR lpszFunction);

// Sample custom data structure for threads to use.
// This is passed by void pointer so it can be any data type
// that can be passed using a single void pointer (LPVOID).
typedef struct MyData {
	int val1;
	int val2;
} MYDATA, *PMYDATA;


int _tmain()
{
	PMYDATA pDataArray[MAX_THREADS];
	DWORD   dwThreadIdArray[MAX_THREADS];
	HANDLE  hThreadArray[MAX_THREADS];

	// Create MAX_THREADS worker threads.

	for (int i = 0; i<MAX_THREADS; i++)
	{
		// Allocate memory for thread data.

		pDataArray[i] = (PMYDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
			sizeof(MYDATA));

		if (pDataArray[i] == NULL)
		{
			// If the array allocation fails, the system is out of memory
			// so there is no point in trying to print an error message.
			// Just terminate execution.
			ExitProcess(2);
		}

		// Generate unique data for each thread to work with.

		pDataArray[i]->val1 = i;
		pDataArray[i]->val2 = i + 100;

		// Create the thread to begin execution on its own.

		hThreadArray[i] = CreateThread(
			NULL,                   // default security attributes
			0,                      // use default stack size  
			MyThreadFunction,       // thread function name
			pDataArray[i],          // argument to thread function 
			0,                      // use default creation flags 
			&dwThreadIdArray[i]);   // returns the thread identifier 


		// Check the return value for success.
		// If CreateThread fails, terminate execution. 
		// This will automatically clean up threads and memory. 

		if (hThreadArray[i] == NULL)
		{
			ErrorHandler(TEXT("CreateThread"));
			ExitProcess(3);
		}
	} // End of main thread creation loop.

	// Wait until all threads have terminated.

	WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);

	// Close all thread handles and free memory allocations.

	for (int i = 0; i<MAX_THREADS; i++)
	{
		CloseHandle(hThreadArray[i]);
		if (pDataArray[i] != NULL)
		{
			HeapFree(GetProcessHeap(), 0, pDataArray[i]);
			pDataArray[i] = NULL;    // Ensure address is not reused.
		}
	}
	system("pause");
	return 0;
}


DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
	HANDLE hStdout;
	PMYDATA pDataArray;

	TCHAR msgBuf[BUF_SIZE];
	size_t cchStringSize;
	DWORD dwChars;

	// Make sure there is a console to receive output results. 

	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
	if (hStdout == INVALID_HANDLE_VALUE)
		return 1;

	// Cast the parameter to the correct data type.
	// The pointer is known to be valid because 
	// it was checked for NULL before the thread was created.

	pDataArray = (PMYDATA)lpParam;

	// Print the parameter values using thread-safe functions.

	StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d\n"),
		pDataArray->val1, pDataArray->val2);
	StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
	WriteConsole(hStdout, msgBuf, (DWORD)cchStringSize, &dwChars, NULL);

	return 0;
}



void ErrorHandler(LPTSTR lpszFunction)
{
	// Retrieve the system error message for the last-error code.

	LPVOID lpMsgBuf;
	LPVOID lpDisplayBuf;
	DWORD dw = GetLastError();

	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		dw,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&lpMsgBuf,
		0, NULL);

	// Display the error message.

	lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
		(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
	StringCchPrintf((LPTSTR)lpDisplayBuf,
		LocalSize(lpDisplayBuf) / sizeof(TCHAR),
		TEXT("%s failed with error %d: %s"),
		lpszFunction, dw, lpMsgBuf);
	MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

	// Free error-handling buffer allocations.

	LocalFree(lpMsgBuf);
	LocalFree(lpDisplayBuf);
}


The MyThreadFunction function avoids the use of the C run-time library (CRT), as many of its functions are not thread-safe, particularly if you are not using the multithreaded CRT. If you would like to use the CRT in a ThreadProc function, use the _beginthreadex function instead.

如果你不是必须使用CRT多线程库, 那么 MyThreadFunction 最好不使用 CRT库, 因为它里面的很多函数是线程不安全的. 在ThreadProc里面你可以用_beginThreadex 函数来代码CRT里面的同功能的函数.

It is risky to pass the address of a local variable if the creating thread exits before the new thread, because the pointer becomes invalid. Instead, either pass a pointer to dynamically allocated memory or make the creating thread wait for the new thread to terminate. Data can also be passed from the creating thread to the new thread using global variables. With global variables, it is usually necessary to synchronize access by multiple threads. For more information about synchronization, see Synchronizing Execution of Multiple Threads.

如果主创线程在创建新线程之前退出.那么使用传址的方法使用一个本地变量是非常危险的,因为已经失效(主创线程已经退出了)了.你可以选择传一个动态分配的内存地址,或等新线程消亡后再允许主创线程退出.数值可以使用全局变更在主创线程与新线程间传递. 使用全局变量要注意同步各个线程中的可用性. 更多的线程同步的知识可以查看Synchronizing Execution of Multiple Threads.

The creating thread can use the arguments to CreateThread to specify the following:

CreateThread的参数有如下几个方面的用途

  • The security attributes for the handle to the new thread. These security attributes include an inheritance flag that determines whether the handle can be inherited by child processes. The security attributes also include a security descriptor, which the system uses to perform access checks on all subsequent uses of the thread's handle before access is granted.
  • 线程句柄的安全属性. 安全属性包含一些继承标识,此标识决定句柄是否可以被子进程继承. 同时也包含对安全信息的描述,这有且于系统对后续对此线程句柄的使用执行安全介入检查.
  • The initial stack size of the new thread. The thread's stack is allocated automatically in the memory space of the process; the system increases the stack as needed and frees it when the thread terminates. For more information, see Thread Stack Size.
  • 线程初始时在堆上占的内存大小. 在进程内线程的堆内存是动态分配的;系统在线程需要内存时为其扩增空间,闲置的时候回收其内存.更多的信息可以查看(Thread Stack Size).
  • A creation flag that enables you to create the thread in a suspended state. When suspended, the thread does not run until the ResumeThreadfunction is called.
  • 创建标识-可以在创建线程时悬挂操作. 当挂起时,线程在ResumeThreadfunction 调用前不会被执行.

You can also create a thread by calling the CreateRemoteThread function. This function is used by debugger processes to create a thread that runs in the address space of the process being debugged.

你也可以调用CreateRemoteThread 函数来创建线程. 这个函数Debugger进行调用,创建一个线程,这个线程在调试进程的地址空间上运行.


-----

翻译暂存,后续修改.



<think>根据提供的代码片段,并没有直接创建线程的代码,而是通过一个初始化函数注册了一系列任务(函数指针数组),然后启动主循环。但是,我们可以推断线程创建可能发生在`mm_app_init`函数内部或者主循环中。在类似网络服务程序中,通常使用多线程或者多任务模型来处理并发请求。 然而,如果我们聚焦于“如何创建线程”,我们可以从标准C和POSIX线程(pthread)的角度来回答。由于代码片段中使用了函数指针数组,我们可以假设这些函数可能是线程的入口函数。 下面我将以POSIX线程(pthread)为例,说明如何创建线程,并解释代码。 ### 使用pthread创建线程的基本步骤 1. 定义线程函数:线程函数必须符合`void* (*start_routine)(void*)`的格式。 2. 声明线程标识符:使用`pthread_t`类型。 3. 调用`pthread_create`函数创建线程。 4. 可选:使用`pthread_join`等待线程结束。 ### 示例代码 ```c #include <stdio.h> #include <pthread.h> // 线程函数原型:接受void*参数,返回void*指针 void* thread_task(void *arg) { char *task_name = (char*)arg; printf("Thread task '%s' is running\n", task_name); // ... 执行任务 ... return NULL; } int main() { pthread_t thread1, thread2; char *task1_name = "Task 1"; char *task2_name = "Task 2"; // 创建线程1 if (pthread_create(&thread1, NULL, thread_task, (void*)task1_name)) { fprintf(stderr, "Error creating thread1\n"); return 1; } // 创建线程2 if (pthread_create(&thread2, NULL, thread_task, (void*)task2_name)) { fprintf(stderr, "Error creating thread2\n"); return 1; } // 等待线程结束 pthread_join(thread1, NULL); pthread_join(thread2, NULL); printf("All threads completed\n"); return 0; } ``` ### 代码解释 1. **线程函数**:`thread_task`是线程的入口函数,它接受一个`void*`参数(可以传递任意类型的数据),并返回一个`void*`指针(通常用于返回结果,这里返回NULL)。 2. **创建线程**:`pthread_create`函数有四个参数: - 第一个参数:指向线程标识符的指针(`pthread_t`类型)。 - 第二个参数:设置线程属性,通常为`NULL`表示默认属性。 - 第三个参数:线程函数的起始地址。 - 第四个参数:传递给线程函数的参数(`void*`类型)。 3. **等待线程结束**:`pthread_join`用于阻塞当前线程,直到指定的线程结束。第二个参数可以接收线程函数的返回值,这里不需要所以设为`NULL`。 ### 在原始代码中的可能应用 在原始代码中,`prvtSrvcTable`数组中的每个函数(如`dhcpCommonTask`)可能都是线程入口函数。在`mm_app_init`函数内部,可能会遍历这个数组,为每个函数创建一个线程。这样,每个服务都在独立的线程中运行。 ### 注意事项 -线程环境下需要考虑资源共享和同步问题,通常使用互斥锁(mutex)、信号量(semaphore)等机制。 - 线程函数中应避免直接终止进程的操作(如`exit`),否则会导致所有线程退出。 §§相关问题§§: 1. 如何在C++中使用std::thread创建线程? 2. 线程同步有哪些常用方法? 3. 什么是线程池?如何实现一个简单的线程池? 4. 多线程编程中如何避免死锁?</think>这段代码本身并没有直接创建线程,而是通过一个框架初始化机制来启动多个服务任务。这些任务很可能是以线程方式运行的。下面我将详细解释这种间接创建线程的方式及其原理: ### 线程创建机制分析 在提供的代码中,线程创建是通过框架函数 `mm_app_init()` 和 `mm_app_main_loop()` 间接完成的。这是一种常见的**任务调度框架**设计模式: ```c // 1. 定义任务函数指针数组 mm_app_func prvtSrvcTable[] = { dhcpCommonTask, dhcpSnoopSaveDynEntryTask, // ... 其他任务函数 }; // 2. 初始化框架参数 mm_app_init_t initFunc = { ._init_private = dhcp_private_init, ._init_public = dhcp_public_init, // ... 其他初始化回调 }; // 3. 框架初始化(隐含线程创建) PFM_IF_FAIL_RET(mm_app_init(&initFunc, prvtSrvcTable, ...)); // 4. 启动主事件循环(隐含线程调度) mm_app_main_loop(); ``` ### 实际线程创建原理 虽然代码中没有直接显示线程创建,但我们可以推断其内部实现逻辑: #### 1. 任务注册阶段 `mm_app_init()` 函数内部会遍历 `prvtSrvcTable` 数组,为每个任务函数创建线程: ```c // 伪代码:框架内部的线程创建 void mm_app_init(...) { for (int i = 0; i < task_count; i++) { pthread_t thread_id; pthread_create(&thread_id, NULL, task_functions[i], NULL); // 将线程ID存入管理结构 } } ``` #### 2. 线程入口函数 任务函数如 `dhcpCommonTask` 是实际的线程入口点: ```c void* dhcpCommonTask(void* arg) { while (running) { // 1. 等待事件(信号量/消息队列) // 2. 处理DHCP公共任务 // 3. 返回事件结果 } return NULL; } ``` #### 3. 主事件循环 `mm_app_main_loop()` 启动调度器主线程: ```c void mm_app_main_loop() { // 创建I/O多路复用描述符 int epoll_fd = epoll_create1(0); // 添加所有需要监听的socket // ... while (1) { // 等待事件(支持超时机制) int n = epoll_wait(epoll_fd, events, MAX_EVENTS, timeout); // 处理网络事件 for (int i = 0; i < n; i++) { dispatch_event(events[i]); } // 处理定时器事件 process_timers(); } } ``` ### 完整线程模型示例 下面是一个简化的实现,展示类似框架如何创建和管理线程: ```c #include <pthread.h> #include <unistd.h> // 任务函数原型 typedef void* (*task_func)(void*); // 框架初始化函数 void framework_init(task_func tasks[], int count) { for (int i = 0; i < count; i++) { pthread_t tid; pthread_create(&tid, NULL, tasks[i], NULL); pthread_detach(tid); // 分离线程,自动回收资源 } } // 示例任务1:DHCP公共处理 void* dhcp_common_task(void* arg) { while (1) { printf("DHCP Common Task running...\n"); sleep(1); } return NULL; } // 示例任务2:DHCP Snooping void* dhcp_snoop_task(void* arg) { while (1) { printf("DHCP Snoop Task running...\n"); sleep(2); } return NULL; } int main() { // 1. 定义任务列表 task_func tasks[] = { dhcp_common_task, dhcp_snoop_task }; // 2. 初始化框架并创建线程 framework_init(tasks, sizeof(tasks)/sizeof(task_func)); // 3. 主线程进入事件循环 while (1) { printf("Main event loop running...\n"); sleep(3); } return 0; } ``` ### 线程模型关键点 1. **任务分配**: - 每个服务对应一个独立的线程 - 线程通过函数指针数组定义 - 框架自动管理线程生命周期 2. **通信机制**: - 线程间通过消息队列/共享内存通信 - 使用互斥锁(mutex)保护共享资源 - 信号量(semaphore)用于任务同步 3. **事件处理**: - 主循环使用epoll/kqueue进行I/O多路复用 - 定时器处理周期性任务 - 信号处理线程处理系统信号 4. **资源管理**: - 线程池技术避免频繁创建销毁 - 优雅退出机制 - 资源泄漏检测 ### 实际应用场景 这种模式广泛应用于: 1. 网络服务守护进程(DHCP/DNS服务器) 2. 嵌入式系统多任务处理 3. 中间件服务框架 4. 电信设备控制平面
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值