在 C 语言中,线程的创建和管理通常通过 POSIX
线程库(pthread)来实现。
线程的生命周期管理
这部分API用于“启动”和“停止”并发任务。
线程创建 (pthread_create) -
功能:这是启动一个新线程的唯一方法。你可以把它想象成“发起一个并发的函数调用”。
1 2 3 4 5 6
| int pthread_create( pthread_t * thread, const pthread_attr_t * attr, void * (*start_routine)(void*), void * arg );
|
-
thread:这是一个“出参”。你传入一个pthread_t变量的地址,pthread_create会把新线程的ID(句柄)填入其中。你将来会用这个ID来join(等待)该线程。
-
start_routine:这是新线程的“main函数”。你告诉create:“请在一个新线程里执行这个函数”。
-
函数的返回值类型必须是void
,参数类型也必须是void。这是C语言中实现“泛型函数指针”的一种方式。
- 你可以在函数内部将void
参数转换为具体类型的指针,然后使用它。 -
返回的void值可以用来传递线程的结果,主线程可以通过pthread_join获取这个返回值。
- void * 和 arg:这是C语言中实现“泛型”的技巧。 - start_routine
必须是一个“接受void*参数,返回void*”的函数。 - arg
则是要传递给start_routine的那个void*参数。 -
返回值:成功返回0,失败返回错误码。
如何传递多个参数?你不能直接传,
标准做法是定义一个struct,把所有参数(如a和b)放进去,然后将这个struct的指针(&args)强制转换为void作为arg传递。线程函数(mythread)接收到void
arg后,再将其强制转换回struct *来解包。
例如, 创建一个新线程来执行 threadFunc 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <pthread.h> #include <iostream> void* threadFunc(void* arg) { int* num = static_cast<int*>(arg); std::cout << "Thread number: " << *num << std::endl; char* ch = static_cast<char*>(arg); std::cout << "Thread character: " << *ch << std::endl; return nullptr; } int main() { pthread_t thread; int threadArg1 = 42; char threadArg2 = 'A'; struct ThreadArgs { int a; char b; } ThreadArgs threadArg = {threadArg1, threadArg2}; pthread_create(&thread, nullptr, threadFunc, static_cast<void*>(&threadArg)); pthread_join(thread, nullptr); return 0; }
|
线程完成/等待 (pthread_join)
功能:让一个线程(通常是主线程)阻塞(睡眠),直到另一个目标线程执行完毕。
1 2 3 4
| int pthread_join( pthread_t thread, void **value_ptr );
|
- thread:你要等待的目标。 - void
**value_ptr:这是API中最令人困惑的部分,我们来拆解它: -
目标线程的start_routine返回一个void
(这是它的“返回值”)。 -
pthread_join函数需要将这个void 返回值写入到main函数的一个变量中。 -
由于C语言中函数参数传递是“值传递”,如果你直接传入一个void
*变量,pthread_join只能修改它的副本,无法修改main函数中的变量。 -
因此,你需要传入这个变量的地址,即一个void
**,这样pthread_join才能通过这个地址修改main函数中的变量。 - 例如:
1 2
| void* retVal; pthread_join(thread, &retVal);
|
- 返回值:成功返回0,失败返回错误码。
线程终止 (pthread_exit)
功能:这是终止当前线程的唯一方法。你可以在当前线程的任何地方调用它来结束线程的执行。
1
| void pthread_exit(void *value_ptr);
|
-
value_ptr:这是线程的“返回值”。当线程调用pthread_exit时,它可以传递一个void
*值作为它的结果。这个值可以被其他线程通过pthread_join获取。 -
注意:调用pthread_exit不会终止整个进程,只会终止当前线程。
互斥原语:锁
这组API用于解决竞态条件问题。
功能:提供互斥(Mutual
Exclusion),确保一个“临界区”在同一时间只被一个线程执行。
1 2
| int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);
|
-
pthread_mutex_lock:尝试获取锁。如果锁已经被其他线程持有,调用线程将阻塞,直到锁可用。
- pthread_mutex_unlock:释放锁。只有持有锁的线程才能调用此函数。 -
返回值:成功返回0,失败返回错误码。
注意, pthread_mutex_t是一个复杂的数据结构,必须在使用前初始化.
可以使用以下两种方式初始化互斥锁: 1. 静态初始化: 用于全局或静态锁
1
| pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
2. 动态初始化: 用于堆上或栈上的锁
1 2
| pthread_mutex_t mutex; pthread_mutex_init(&mutex, nullptr);
|
例如,
使用互斥锁保护临界区:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <pthread.h> #include <iostream> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int sharedResource = 0; void* threadFunc(void* arg) { pthread_mutex_lock(&mutex); sharedResource++; std::cout << "Shared Resource: " << sharedResource << std::endl; pthread_mutex_unlock(&mutex); return nullptr; }
|
协作原语:条件变量
这组API用于解决等待/通知(协作)问题。
功能:允许一个线程睡眠(wait),直到某个“条件”为真,而另一个线程在使该“条件”为真后,通知(signal)睡眠的线程醒来。
1 2
| int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_signal(pthread_cond_t *cond);
|
- pthread_cond_wait(&cond, &lock)
这一行代码会原子地执行以下三步: -
释放传入的锁(&lock)。(这允许“通知者”线程能有机会进入临界区去修改ready)
- 使当前线程睡眠(等待&cond上的信号)。 -
(…当被唤醒后…)重新获取那个锁(&lock),然后才从wait函数返回。 这里
wait 和 signal 的标准模式是:
条件变量必须和一个互斥锁(Mutex)配对使用,以保护“条件”本身(例如一个
ready 标志)
等待者(Waiter)的代码:1 2 3 4 5 6
| pthread_mutex_lock(&lock); while (ready == 0) { pthread_cond_wait(&cond, &lock); }
pthread_mutex_unlock(&lock);
|
通知者(Signaler)的代码:1 2 3 4
| pthread_mutex_lock(&lock); ready = 1; pthread_cond_signal(&cond); pthread_mutex_unlock(&lock);
|