博主介绍:程序喵大人
- 35- 资深C/C++/Rust/Android/iOS客户端开发
- 10年大厂工作经验
- 嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手
- 《C++20高级编程》《C++23高级编程》等多本书籍著译者
- 更多原创精品文章,首发gzh,见文末
- 👇👇记得订阅专栏,以防走丢👇👇
😉C++基础系列专栏
😃C语言基础系列专栏
🤣C++大佬养成攻略专栏
🤓C++训练营
👉🏻个人网站
多线程编程是一种在程序中同时执行多个线程(子任务)的技术。
线程是程序中的一个执行单元,每个线程都有自己的执行路径和上下文。
一个进程可以包含多个线程,这些线程可以并发执行,共享进程的内存空间和资源,但拥有各自的栈空间和寄存器状态。
为什么要多线程?
举个例子,100个人需要坐车从故宫到颐和园,一个车可以坐5个人,如果我们只有1个车,那需要20个往返,非常耗费时间。而如果我们有10个车,那就只需要2个往返的时间,肯定更快。
多线程的优势主要有:
- 并发性:多线程使程序可以同时执行多个任务,提高程序的并发性,可以更充分地利用多核处理器。
- 响应性:多线程可以使程序响应用户输入或外部事件,内部复杂的逻辑在其它线程执行,保持界面的活跃性。
- 资源共享:多线程允许线程之间共享内存和资源,可以降低资源消耗,提高效率。
- 模块化:多线程可以将复杂任务分解为多个独立的线程,每个线程运行一部分任务,使程序更易于维护和扩展,且提高程序的效率。
POSIX线程库(pthread)
POSIX线程库(pthread)是一个在UNIX和类UNIX系统(如Linux)上实现的标准多线程API。它提供了一组函数来创建、管理和控制线程,以及实现线程之间的同步。
常用的pthread函数包括:
- 线程管理函数
pthread_create
:创建一个新线程,并使其从指定的函数开始运行。pthread_exit
:终止当前线程,返回一个值给pthread_join()
。pthread_join
:阻塞调用线程,直到指定的线程终止,并获取其返回值。pthread_self
:返回调用线程的线程ID。pthread_detach
:将指定的线程设置为分离状态,当线程执行完毕后会自动释放资源。
- 线程同步函数
pthread_mutex_init
:初始化一个互斥锁(mutex),用于保护共享资源。pthread_mutex_lock
:锁定互斥锁。pthread_mutex_unlock
:解锁互斥锁。pthread_mutex_destroy
:销毁互斥锁,释放其使用的资源。pthread_cond_wait
:等待条件变量被满足。pthread_cond_signal
:唤醒一个等待该条件变量的线程。
线程的创建与同步
线程的创建
在C语言中,使用pthread_create
函数来创建新线程。函数的基本用法如下:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
thread
:用于存储新线程的标识符。attr
:线程属性,通常可以设置为NULL。start_routine
:新线程的入口函数,该函数接受一个void*
参数并返回void*
。arg
:传递给start_routine
的参数。
以下示例,演示如何创建一个新线程:
#include <stdio.h>
#include <pthread.h>
void* print_hello(void* arg) {
printf("Hello from new thread!\n");
return NULL;
}
int main() {
pthread_t tid; // 线程标识符
pthread_create(&tid, NULL, print_hello, NULL);
pthread_join(tid, NULL); // 等待新线程结束
printf("Main thread: New thread has finished.\n");
return 0;
}
线程的同步
当多个线程同时访问共享资源时,可能会出现竞争条件,导致数据错误。为了避免竞争条件,可以使用互斥锁(Mutex)进行线程同步。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex;
int shared_counter = 0;
void* increment_counter(void* arg) {
for (int i = 0; i < 1000; ++i) {
pthread_mutex_lock(&mutex);
shared_counter++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
pthread_mutex_init(&mutex, NULL);
pthread_create(&thread1, NULL, increment_counter, NULL);
pthread_create(&thread2, NULL, increment_counter, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Final counter value: %d\n", shared_counter);
pthread_mutex_destroy(&mutex);
return 0;
}
在这个示例中,两个线程同时运行,每个线程都尝试增加共享计数器shared_counter
的值。
通过使用互斥锁,我们确保了每次只有一个线程可以访问和修改shared_counter
,从而避免了竞态条件。
线程安全与互斥锁
线程安全是指多线程程序在并发执行时能够正确访问和修改共享资源,而不会导致数据不一致或程序崩溃等奇奇怪怪的问题。
而为了保证线程安全,通常都需要使用同步机制,如互斥锁(Mutex)。
互斥锁是一种用于保护共享资源的同步机制。当一个线程持有互斥锁时,其他线程无法访问该资源,直到锁被释放为止。这确保了资源在任何时候都只能被一个线程访问,从而避免了竞竞条件。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex;
int shared_data = 0;
void* thread_function(void* arg) {
for (int i = 0; i < 1000; ++i) {
pthread_mutex_lock(&mutex);
// 访问和修改共享数据
shared_data++;
printf("Thread %ld: shared_data = %d\n", pthread_self(), shared_data);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t threads[2];
pthread_mutex_init(&mutex, NULL);
pthread_create(&threads[0], NULL, thread_function, NULL);
pthread_create(&threads[1], NULL, thread_function, NULL);
pthread_join(threads[0], NULL);
pthread_join(threads[1], NULL);
printf("Final shared_data value: %d\n", shared_data);
pthread_mutex_destroy(&mutex);
return 0;
}
在这个示例中,两个线程同时运行,并尝试访问和修改共享数据shared_data
。通过使用互斥锁,我们确保了每次只有一个线程可以访问和修改shared_data
,从而保证了线程安全。
为了避免竞争条件,保证线程安全,除了使用互斥锁,我们还可以使用原子变量,也推荐你了解下原子操作。
码字不易,欢迎大家点赞,关注,评论,谢谢!
C++训练营
专为校招、社招3年工作经验的同学打造的1V1 C++训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得大厂offer!