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 #include 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 #include 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 #include #include #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 #include #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 避免数据竞争
数据竞争发生在多个线程同时访问共享数据时,其中至少有一个线程进行写操作。如果没有同步机制,可能导致数据的不一致。常见的解决方法是使用 互斥锁 或 原子操作。