> 技术文档 > linux c语言进阶 - 线程,通信方式,安全方式(多并发)

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
    失败 : 返回 错误码

注意:

    一旦子线程创建成功,则会被独立调度执行,并且与其他线程 并发执行
创建多个线程时,⼀般由主线程统⼀创建,并等待释放资源或者分离线程, 不要递归创建。
在编译时需要链接 -lpthread

task.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 线程的生命周期

线程的生命周期分为以下几个阶段:

  1. 创建阶段:调用 pthread_create() 创建线程。

  2. 执行阶段:线程开始执行它的任务,执行完线程函数后线程自动终止。

  3. 终止阶段:线程执行完成后会通过 pthread_exit() 或返回函数退出,终止其生命周期。

  4. 等待阶段:线程通过 pthread_join() 被主线程或其他线程等待直到完成。

  5. 清理阶段:线程退出时会自动进行清理,释放资源。

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 避免数据竞争

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