linux c语言进阶 - 线程,通信方式,安全方式(多并发)
线程
1. 概念
线程是程序中执行的最小单位,它属于进程的一部分。每个进程至少包含一个线程(主线程)。线程共享进程的资源(如内存空间、文件描述符等),但每个线程有自己的堆栈、程序计数器等。多线程编程允许程序在同一时间并行执行多个任务。
1.1 线程与进程的区别
进程 是操作系统进行资源分配和调度的基本单位,每个进程有独立的内存空间(4G)。
线程 是程序执行的基本单位,是在进程内运行的独立执行流。线程之间共享进程的内存空间、打开的文件、信号量等资源。
1.2 线程的作用
1. 并发执行:在多核cpu的下,可以通知执行多个线程,效率高。 比如16核,那可以通过调度16个线程(多个进程)
2. 异步执行: 不用同步执行。**等会写代码来测试。**
2. 线程的使用
2.1 线程的创建
pthread_create()
函数用于创建一个线程。
int pthread_create(pthread_t *tip, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数:
1. thread : 线程 ID 变量指针
2. attr : 线程属性,默认属性可设置为 NULL
3. start_routine : 线程执行函数
4. arg : 线程执行函数的参数返回值:
成功 : 返回 0
失败 : 返回 错误码注意:
一旦子线程创建成功,则会被独立调度执行,并且与其他线程 并发执行
创建多个线程时,⼀般由主线程统⼀创建,并等待释放资源或者分离线程, 不要递归创建。
在编译时需要链接 -lpthreadtask.json(要修改编译语句)
"args": ["-fdiagnostics-color=always","-g","${file}","-o","${fileDirname}/${fileBasenameNoExtension}","-lpthread"],
-
pthread_t
是线程的标识符。 -
thread_function
是线程的执行函数。 -
pthread_join()
用于等待线程的结束。
int pthread_join(pthread_t thread, void **retval);
参数
thread : 线程 ID
retval : 获取线程退出值的指针返回值
成功 : 返回 0
失败 : 返回 错误码注意
线程分离函数不会阻塞线程的执行
-
pthread_exit()退出
void pthread_exit(void *retval);
参数:
retval : 线程返回值,通过指针传递
返回值:
成功 :返回 0
失败 : 返回 -1
注意:
1.当主线程调用 pthread_exit 函数时,进程不会结束,也不会导致其他子线程退出
2.任何线程调用 exit 函数会让进程结束
3.在线程函数内部显式调用 `pthread_exit()退出线程。
4.可以用于线程任意位置退出(不一定要在函数末尾)。
#include <stdio.h>
#include <pthread.h>void *demo(void *arg)
{printf("子线程!\n");return NULL;
}/*线程*/
int main(int argc, char const *argv[])
{// 创建一个pthread_t tid;pthread_create(&tid, NULL, demo, NULL);pthread_join(tid, NULL);return 0;
}
2.2 线程的生命周期
线程的生命周期分为以下几个阶段:
创建阶段:调用
pthread_create()
创建线程。执行阶段:线程开始执行它的任务,执行完线程函数后线程自动终止。
终止阶段:线程执行完成后会通过
pthread_exit()
或返回函数退出,终止其生命周期。等待阶段:线程通过
pthread_join()
被主线程或其他线程等待直到完成。清理阶段:线程退出时会自动进行清理,释放资源。
2.3 线程的销毁
自动销毁:线程结束时,系统会回收资源。
手动销毁:使用
pthread_cancel()
可以取消线程,但这需要特别小心,避免线程处于不确定状态。
3. 线程间的通讯(信号,信道,消息队列,共享内存,信号量)
linux c语言进阶 - 进程,通信方式-CSDN博客此文章介绍了一些进程的相关方法,需要的同志可以看一下。
3.1 共享内存
由于线程共享进程的内存空间,它们可以直接通过共享内存进行通信。不同线程可以修改共享变量或数据结构。但这也引发了 线程安全 的问题。
3.2 条件变量(Condition Variable)
条件变量是一种线程同步机制,用于在某个条件满足时唤醒线程。条件变量常与互斥锁一起使用。
#include <pthread.h>
#include <stdio.h>pthread_mutex_t mutex;
pthread_cond_t cond;void *thread_function(void *arg) {pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex); // 等待条件变量printf("Condition met, thread proceeding\n");pthread_mutex_unlock(&mutex);return NULL;
}int main() {pthread_t thread_id;pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);pthread_create(&thread_id, NULL, thread_function, NULL);// 模拟条件满足pthread_mutex_lock(&mutex);pthread_cond_signal(&cond); // 唤醒等待的线程pthread_mutex_unlock(&mutex);pthread_join(thread_id, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}
3.3 信号量(Semaphore)
信号量是另一种线程同步机制,通常用于控制多个线程访问共享资源的数量。(有名无名)
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>#define NUM_THREADS 4
#define TOTAL_TICKETS 1000int ticket_count = TOTAL_TICKETS;
sem_t semaphore;void* sell_tickets(void* arg) {while (1) {sem_wait(&semaphore); // 获取信号量if (ticket_count <= 0) {sem_post(&semaphore); // 释放信号量break;}// 模拟售票处理时间struct timespec sleep_time = {0, 1000000}; // 1毫秒nanosleep(&sleep_time, NULL);printf("线程%ld卖出第%d张票\n", (long)arg, ticket_count);ticket_count--;sem_post(&semaphore); // 释放信号量}return NULL;
}int main() {pthread_t threads[NUM_THREADS];sem_init(&semaphore, 0, 1); // 初始值为1的二进制信号量// 创建售票线程for (long i = 0; i < NUM_THREADS; i++) {pthread_create(&threads[i], NULL, sell_tickets, (void*)i);}// 等待所有线程完成for (int i = 0; i < NUM_THREADS; i++) {pthread_join(threads[i], NULL);}sem_destroy(&semaphore);printf("所有票已售罄\n");return 0;
}
3.4 互斥锁(Mutex)
互斥锁用于保护共享资源,避免多个线程同时访问共享资源,从而导致数据不一致。
#include <stdio.h>
#include <pthread.h>#define NUM_THREADS 4
#define TOTAL_TICKETS 1000int ticket_count = TOTAL_TICKETS;
pthread_mutex_t lock;void* sell_tickets(void* arg) {while (1) {pthread_mutex_lock(&lock); // 加锁if (ticket_count <= 0) {pthread_mutex_unlock(&lock); // 解锁break;}// 模拟售票处理时间struct timespec sleep_time = {0, 1000000}; // 1毫秒nanosleep(&sleep_time, NULL);printf("线程%ld卖出第%d张票\n", (long)arg, ticket_count);ticket_count--;pthread_mutex_unlock(&lock); // 解锁}return NULL;
}int main() {pthread_t threads[NUM_THREADS];pthread_mutex_init(&lock, NULL);// 创建售票线程for (long i = 0; i < NUM_THREADS; i++) {pthread_create(&threads[i], NULL, sell_tickets, (void*)i);}// 等待所有线程完成for (int i = 0; i < NUM_THREADS; i++) {pthread_join(threads[i], NULL);}pthread_mutex_destroy(&lock);printf("所有票已售罄\n");return 0;
}
4. 线程安全
线程安全是指在多线程环境中,多个线程并发执行时,不会产生数据竞争、死锁等问题,保证程序的正确性
4.1 避免数据竞争
数据竞争发生在多个线程同时访问共享数据时,其中至少有一个线程进行写操作。如果没有同步机制,可能导致数据的不一致。常见的解决方法是使用 互斥锁 或 原子操作。