Linux----线程

张裕  金牌会员 | 2025-2-25 00:28:41 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 702|帖子 702|积分 2106

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x

一、基础概念对比

特性进程 (Process)线程 (Thread)资源分配资源分配的根本单位(独立所在空间)共享进程资源调度单位操作体系调度单位CPU调度的最小单位创建开销高(需复制父进程资源)低(共享进程资源)通讯方式管道、共享内存、消息队列等IPC共享全局变量(需同步机制)隔离性内存隔离,安全性高共享内存,需处理竞争条件典范组成代码段+数据段+堆栈段+PCB线程ID+寄存器组+栈+线程控制块TCB
二、线程组成详解

1. 核心组件

  1. struct thread_struct {
  2.     pthread_t tid;           // 线程ID (8字节)
  3.     void* stack_base;        // 栈基地址 (8字节)
  4.     size_t stack_size;       // 栈大小 (Linux默认8MB)
  5.     void* (*start_routine)(void*); // 入口函数指针
  6.     void* arg;               // 入口函数参数
  7.     // 寄存器组保存区 (约52个寄存器,约416字节)
  8.     // 包括:PC、SP、通用寄存器、浮点寄存器等
  9. };
复制代码
2. 关键特征



  • 线程ID:pthread_t 范例,进程内唯一
  • 独立栈空间:每个线程拥有独立调用栈
  • 共享资源:全局变量、堆内存、文件形貌符等

三、线程创建与管理

1. 创建函数原型

  1. #include <pthread.h>
  2. int pthread_create(pthread_t *thread,
  3.                    const pthread_attr_t *attr,
  4.                    void *(*start_routine)(void *),
  5.                    void *arg);
复制代码
参数详解表

参数范例作用说明threadpthread_t*输出参数,存储新线程IDattrpthread_attr_t*线程属性(NULL利用默认属性):<br>▪ 栈巨细<br>▪ 调度策略<br>▪ 分离状态start_routinevoid* (*)(void*)线程入口函数(返回值为线程退出状态)argvoid*传递给入口函数的参数 返回值



  • 成功返回0
  • 失败返回错误码(非errno值,需用strerror转换)




2. 编译指令

  1. gcc program.c -lpthread -o program  # 必须链接pthread库
复制代码

四、线程生命周期管理

1. 线程终止方式

(1) 显式调用退出函数

  1. void* worker(void* arg) {
  2.     int* heap_result = malloc(sizeof(int));
  3.     *heap_result = 100;
  4.     pthread_exit((void*)heap_result);  // 正确方式:堆内存传递
  5.     // static int static_result = 200;  // 替代方案:静态变量
  6.     // pthread_exit((void*)&static_result);
  7. }
复制代码
关键特性


  • 状态值通过pthread_join()获取
  • 返回值必须利用堆内存或全局/静态变量
  • 主线程调用时仅竣事自身实行流
(2) 入口函数返回

  1. void* worker(void* arg) {
  2.     static int result = 200;  // 必须使用静态存储期变量
  3.     return (void*)&result;     // 等效于pthread_exit()
  4. }
复制代码
禁止举动
  1. int local_var = 300;
  2. return (void*)&local_var;  // 错误!栈空间失效
复制代码
(3) 被其他线程取消

  1. // 取消请求端
  2. pthread_cancel(target_tid);
  3. // 被取消线程端
  4. void* worker(void* arg) {
  5.     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
  6.     while(1) {
  7.         pthread_testcancel();  // 设置取消点
  8.         /* 长时间操作 */
  9.     }
  10. }
复制代码
取消范例对比
范例举动特征设置函数PTHREAD_CANCEL_DEFERRED耽误到下一个取消点pthread_setcanceltype()PTHREAD_CANCEL_ASYNCHRONOUS立即终止(可能破坏数据)pthread_setcanceltype() (4) 进程级终止

  1. int main() {
  2.     pthread_t tid;
  3.     pthread_create(&tid, NULL, worker, NULL);
  4.    
  5.     // return 0;        // 错误!触发exit()终止所有线程
  6.     pthread_exit(NULL); // 正确:仅结束主线程
  7. }
复制代码
进程终止规则


  • exit()立即终止整个进程
  • 主线程return会隐式调用exit()
  • 建议主线程始终利用pthread_exit()
2. 状态接纳机制

(1) 壅闭接纳(Joinable模式)

  1. void* status;
  2. int ret = pthread_join(tid, &status);
  3. if(ret == 0) {
  4.     printf("退出码: %d\n", *(int*)status);
  5.     free(status);  // 必须释放堆内存
  6. }
复制代码
限制条件


  • 每个线程只能被join一次
  • 已分离线程无法join
(2) 自动接纳(Detached模式)

  1. // 创建时设置分离属性
  2. pthread_attr_t attr;
  3. pthread_attr_init(&attr);
  4. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  5. pthread_create(&tid, &attr, worker, NULL);
  6. // 运行时分离
  7. pthread_detach(existing_tid);
复制代码
特性


  • 线程终止后自动接纳资源
  • 无法获取返回值
  • 实用于后台任务线程
五、线程资源管理

1. 清算函数机制

  1. void cleanup(void* arg) {
  2.     printf("清理资源: %p\n", arg);
  3.     free(arg);
  4. }
  5. void* worker(void* arg) {
  6.     void* res = malloc(1024);
  7.     pthread_cleanup_push(cleanup, res);  // 注册清理函数
  8.    
  9.     // 可能被取消的代码段
  10.     while(1) {
  11.         pthread_testcancel();
  12.         /* 临界操作 */
  13.     }
  14.    
  15.     pthread_cleanup_pop(1);  // 执行清理并出栈
  16.     return NULL;
  17. }
复制代码
触发条件

  • pthread_cleanup_pop(非零值)
  • 线程通过pthread_exit()退出
  • 被其他线程取消
编码规范


  • push/pop必须成对出现
  • 建议在可能被取消的代码段前注册
  • 栈式管理(后进先出)
2. 属性管理

(1) 完备属性设置流程

  1. pthread_attr_t attr;
  2. pthread_attr_init(&attr);  // 初始化
  3. // 设置分离状态
  4. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  5. // 设置栈大小(2MB示例)
  6. size_t stack_size = 2 * 1024 * 1024;
  7. void* stack_addr = malloc(stack_size);
  8. pthread_attr_setstack(&attr, stack_addr, stack_size);
  9. pthread_create(&tid, &attr, worker, NULL);
  10. pthread_attr_destroy(&attr);  // 销毁属性
复制代码
(2) 常用属性API

函数功能形貌pthread_attr_setdetachstate设置分离/结合状态pthread_attr_setstacksize设置线程栈巨细pthread_attr_setguardsize设置栈溢出保护区巨细pthread_attr_setschedpolicy设置调度策略(FIFO/RR等) 六、线程同步机制

1. 互斥锁完备实现

  1. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  2. void* thread_func(void* arg) {
  3.     pthread_mutex_lock(&mutex);
  4.     /* 临界区操作 */
  5.     pthread_mutex_unlock(&mutex);
  6.     return NULL;
  7. }
  8. // 动态初始化方式
  9. pthread_mutex_init(&mutex, NULL);
  10. /* ... */
  11. pthread_mutex_destroy(&mutex);
复制代码
2. 同步机制对比

机制实用场景优点缺点互斥锁共享资源访问控制简朴高效可能产生死锁读写锁读多写少场景提高读并发性能写线程可能饿死条件变量线程间状态关照准确唤醒机制需配合互斥锁利用信号量资源数量控制跨进程可用功能相对基础 3. 全局变量竞争办理方案

  1. pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
  2. int global_counter = 0;
  3. void* counter_thread(void* arg) {
  4.     for(int i=0; i<100000; ++i) {
  5.         pthread_mutex_lock(&mutex);
  6.         global_counter++;  // 原子操作
  7.         pthread_mutex_unlock(&mutex);
  8.     }
  9.     return NULL;
  10. }
复制代码
优化建议


  • 尽量减小临界区范围
  • 避免嵌套锁
  • 利用trylock避免死锁
七、高级主题与最佳实践

1. 返回值处理规范

(1) 简朴状态码

  1. // 传递整型值
  2. pthread_exit((void*)(intptr_t)error_code);
  3. // 接收端
  4. int code = (int)(intptr_t)status;
复制代码
(2) 复杂数据结构

  1. struct Result {
  2.     int code;
  3.     char message[256];
  4. };
  5. void* worker(void* arg) {
  6.     struct Result* res = malloc(sizeof(struct Result));
  7.     /* 填充数据 */
  8.     pthread_exit(res);
  9. }
  10. // 接收端
  11. struct Result* res = (struct Result*)status;
  12. free(res);
复制代码
2. 线程设计准则


  • 资源管理三原则

    • 谁分配谁释放
    • 退出前释放非共享资源
    • 利用RAII模式管理资源

  • 锁利用规范
    1. // 推荐加锁方式
    2. pthread_mutex_lock(&mutex);
    3. do {
    4.     /* 临界区操作 */
    5. } while(0);
    6. pthread_mutex_unlock(&mutex);
    7. // 避免的写法
    8. if(condition) pthread_mutex_unlock(&mutex);  // 易漏解锁
    复制代码
  • 错误处理模板
    1. int ret = pthread_create(&tid, NULL, worker, NULL);
    2. if(ret != 0) {
    3.     fprintf(stderr, "线程创建失败: %s\n", strerror(ret));
    4.     exit(EXIT_FAILURE);
    5. }
    复制代码
3. 调试本领


  • 死锁检测

    • 利用pthread_mutex_trylock()测试锁状态
    • 记载加锁顺序
    • 利用Valgrind的Helgrind工具

  • 性能分析
    1. # 使用perf分析锁竞争
    2. perf record -g -- ./program
    3. perf report
    复制代码
     八、完备生命周期图示

  1. graph TD
  2.     A[线程创建] --> B{执行阶段}
  3.     B -->|正常完成| C[资源回收]
  4.     B -->|被取消| D[清理处理]
  5.     C --> E[线程终止]
  6.     D --> E
  7.     E --> F[系统回收TID]
  8.    
  9.     style B fill:#f9f,stroke:#333,stroke-width:2px
  10.     style C fill:#9f9,stroke:#333
  11.     style D fill:#f99,stroke:#333
  12.    
  13.     subgraph 关键状态
  14.     B
  15.     C
  16.     D
  17.     end
复制代码
九、常见标题办理方案

1. 僵尸线程标题

征象:线程终止但未接纳,占用体系资源
办理方案
  1. // 方案1:及时join
  2. void* retval;
  3. pthread_join(tid, &retval);
  4. free(retval);
  5. // 方案2:设置分离属性
  6. pthread_attr_t attr;
  7. pthread_attr_init(&attr);
  8. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  9. pthread_create(&tid, &attr, worker, NULL);
复制代码
2. 返回值内存泄漏

错误示例
  1. void* worker() {
  2.     int result = 42;
  3.     pthread_exit(&result);  // 栈内存泄露!
  4. }
复制代码
精确实践
  1. void* worker() {
  2.     int* result = malloc(sizeof(int));  // 堆内存
  3.     *result = 42;
  4.     pthread_exit(result);
  5. }
复制代码
3. 取消点设置不足

标题表现:取消请求长期不响应
优化方案
  1. void* worker(void* arg) {
  2.     pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  3.     while(1) {
  4.         pthread_testcancel();  // 每循环添加取消点
  5.         /* 长时间操作 */
  6.     }
  7.     return NULL;
  8. }
复制代码
十、扩展知识

1. 线程与进程对比

特性进程线程资源开销高(独立所在空间)低(共享所在空间)通讯方式管道、共享内存、信号等全局变量、互斥锁、条件变量容错性一个进程瓦解不影响其他线程瓦解导致整个进程终止上下文切换成本高低 2. 可重入函数设计

安全函数特征


  • 不利用静态变量
  • 不调用非可重入函数
  • 所有数据通过参数传递
示例对比
  1. // 不安全版本
  2. char* strtok(char* str, const char* delim) {
  3.     static char* buffer;  // 静态变量
  4.     /* ... */
  5. }
  6. // 可重入版本
  7. char* strtok_r(char* str, const char* delim, char** saveptr) {
  8.     /* 使用传入的saveptr保存状态 */
  9. }
复制代码
十一、信号量

一、信号量概述


  • 本质:计数器+等候队列,用于控制多线程/进程对共享资源的访问
  • 分类:

    • 无名信号量:内存中匿名对象,用于线程间同步
    • 有名信号量:文件体系可见,用于进程间同步

二、核心API详解

1. 无名信号量操作

  1. int sem_init(sem_t *sem, int pshared, unsigned int value);
复制代码


  • pshared:0表示线程间共享,非0表示进程间共享
  • value:信号量初始值(可用资源数)
2. 有名信号量操作

  1. sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
  2. int sem_unlink(const char *name);  // 删除系统残留信号量
复制代码
3. 通用操作函数

  1. // P操作(申请资源)
  2. int sem_wait(sem_t *sem);       // 阻塞等待
  3. int sem_trywait(sem_t *sem);    // 非阻塞尝试
  4. // V操作(释放资源)
  5. int sem_post(sem_t *sem);
  6. // 销毁信号量
  7. int sem_destroy(sem_t *sem);    // 无名信号量
  8. int sem_close(sem_t *sem);      // 有名信号量
复制代码
三、利用框架

  1. // 无名信号量使用流程
  2. sem_t sem;
  3. sem_init(&sem, 0, 1);  // 初始化
  4. sem_wait(&sem);         // P操作
  5. // 临界区操作...
  6. sem_post(&sem);         // V操作
  7. sem_destroy(&sem);      // 销毁
  8. // 有名信号量使用流程
  9. sem_t *sem = sem_open("/mysem", O_CREAT, 0644, 1);
  10. sem_wait(sem);
  11. // 临界区操作...
  12. sem_post(sem);
  13. sem_close(sem);
  14. sem_unlink("/mysem");  // 最后使用后删除
复制代码
四、线程同步示例

  1. #include <semaphore.h>
  2. #include <pthread.h>
  3. sem_t sem;
  4. int shared_data;
  5. void* thread_func(void* arg) {
  6.     sem_wait(&sem);
  7.     // 操作共享资源
  8.     shared_data++;
  9.     sem_post(&sem);
  10.     return NULL;
  11. }
  12. int main() {
  13.     sem_init(&sem, 0, 1);
  14.     pthread_t tid;
  15.     pthread_create(&tid, NULL, thread_func, NULL);
  16.    
  17.     sem_wait(&sem);
  18.     printf("Shared data: %d\n", shared_data);
  19.     sem_post(&sem);
  20.    
  21.     pthread_join(tid, NULL);
  22.     sem_destroy(&sem);
  23.     return 0;
  24. }
复制代码
五、线程属性管理

  1. // 设置分离属性
  2. pthread_attr_t attr;
  3. pthread_attr_init(&attr);
  4. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  5. // 或在线程创建后设置
  6. pthread_detach(thread_id);
  7. // 主动让出CPU
  8. pthread_yield();  // 或使用 usleep()
复制代码
六、死锁专题

须要条件(需同时满意):


  • 互斥访问
  • 请求并保持
  • 不可剥夺
  • 循环等候
避免策略:



  • 资源有序分配法
  • 银行家算法
  • 设置超时机制(利用sem_timedwait)
  • 避免嵌套锁申请
  • 利用valgrind等工具检测
七、留意事项


  • 信号量初始值决定举动:

    • 二进制信号量:初始值为1
    • 计数信号量:初始值>1

  • 必须检查API返回值:
    1. if(sem_post(&sem) == -1) {
    2.     perror("sem_post failed");
    3. }
    复制代码
  • 销毁前确保:

    • 没有线程壅闭在信号量上
    • 不能销毁已销毁的信号量

  • 有名信号量的命名:

    • 必须以斜杠开头(如"/mysem")
    • 长度限制(通常NAME_MAX-4)

八、补充说明


  • POSIX vs System V信号量:

    • POSIX更轻量,接口更轻便
    • System V支持更复杂的控制

  • 信号量与互斥锁的区别:
       特性信号量互斥锁所有者无有(锁定线程)可递增值是否跨进程利用支持(有名)一般不支持



练习、创建两个线程,线程1 打印 hello,线程2 打印 world,预期效果在屏幕上严格打印hello world
  1. #include<stdio.h>
  2. #include<pthread.h>
  3. #include<errno.h>
  4. #include<unistd.h>
  5. #include<semaphore.h>
  6. char buf[1024];
  7. sem_t sem_w;
  8. sem_t sem_r;
  9. void *do_something(void *arg)
  10. {
  11.         while(1)
  12.         {
  13.                 sem_wait(&sem_w);
  14.                 printf("hello ");
  15.                 sem_post(&sem_r);
  16.                 sleep(1);
  17.         }
  18.         return NULL;
  19. }
  20. void *do_something1(void *arg)
  21. {
  22.         while(1)
  23.         {
  24.                 sem_wait(&sem_r);
  25.                 printf("world!\n");
  26.                 sem_post(&sem_w);
  27.                 sleep(1);
  28.         }
  29.         return NULL;
  30. }
  31. int main(int argc, const char *argv[])
  32. {
  33.         pthread_t tid1, tid2;
  34.         int ret, ret1;
  35.         sem_init(&sem_w,0,1);
  36.         sem_init(&sem_r,0,0);
  37.         if ((ret = pthread_create(&tid1, NULL, do_something, NULL)) != 0)
  38.         {
  39.                 errno = ret;
  40.                 perror("pthread_create for do_something fail");
  41.                 return -1;
  42.         }
  43.         if ((ret1 = pthread_create(&tid2, NULL, do_something1, NULL)) != 0)
  44.         {
  45.                 errno = ret1;
  46.                 perror("pthread_create for do_something1 fail");
  47.                 return -1;
  48.         }
  49.         pthread_join(tid1, NULL);
  50.         pthread_join(tid2, NULL);
  51.     return 0;
  52. }
复制代码
十二、练习

    练习1:创建一个线程

  1. #include<stdio.h>
  2. #include<pthread.h>
  3. #include<errno.h>
  4. #include<unistd.h>
  5. void * do_something(void *arg)
  6. {
  7.         printf("do copy file---\n");
  8.         return NULL;
  9. }
  10. int main(int argc, const char *argv[])
  11. {
  12.         pthread_t tid;
  13.         int ret;
  14.         if((ret = pthread_create(&tid,NULL,do_something,NULL)) != 0)
  15.         {
  16.                 errno = ret;
  17.                 perror("pthread_create fail");
  18.                 return -1;
  19.         }
  20.         printf("-----main-------\n");
  21.         sleep(1);
  22.         return 0;
  23.        
  24.        
  25.         return 0;
  26. }
复制代码
  练习2:创建多个线程

  1. #include<stdio.h>
  2. #include<pthread.h>
  3. #include<errno.h>
  4. #include<unistd.h>
  5. void * do_one(void *arg)
  6. {
  7.         printf("pthread 1 pid = %d\n",getpid());
  8.         return NULL;
  9. }
  10. void * do_two(void *arg)
  11. {
  12.         printf("pthread 2 pid = %d\n",getpid());
  13.         return NULL;
  14. }
  15. void * do_three(void *arg)
  16. {
  17.         printf("pthread 3 pid = %d\n",getpid());
  18.         return NULL;
  19. }
  20. typedef void *(*thread_cb_t)(void*);
  21. int main(int argc, const char *argv[])
  22. {
  23.         printf("---main---  pid = %d\n",getpid());
  24.        
  25.        
  26.         pthread_t tid[3];
  27.         int ret;
  28.         thread_cb_t func[3] = {do_one,do_two,do_three};
  29.         int i = 0;
  30.         for(i = 0;i < 3;i++)
  31.         {
  32.                 if((ret = pthread_create(&tid[i],NULL,func[i],NULL)) != 0)
  33.                 {
  34.                         errno = ret;
  35.                         perror("pthread1_create fail");
  36.                         return -1;
  37.                 }
  38.         }
  39.                 sleep(1);
  40.         return 0;
  41.        
  42.         return 0;
  43. }
复制代码
 练习3:线程的关闭

  1. #include<stdio.h>
  2. #include<pthread.h>
  3. #include<errno.h>
  4. #include<unistd.h>
  5. void * do_something(void *arg)
  6. {
  7.         static int ret = 100;
  8.         printf("do copy file---\n");
  9.         //pthread_exit("i am dead\n");
  10.         pthread_exit(&ret);
  11.         //return NULL;
  12. }
  13. int main(int argc, const char *argv[])
  14. {
  15.         pthread_t tid;
  16.         int ret;
  17.         if((ret = pthread_create(&tid,NULL,do_something,NULL)) != 0)
  18.         {
  19.                 errno = ret;
  20.                 perror("pthread_create fail");
  21.                 return -1;
  22.         }
  23.         printf("-----main-------\n");
  24.         int *retval;
  25.         //char *retval;
  26.         pthread_join(tid,(void **)&retval);
  27.         //printf("*retval = %s\n",retval);
  28.         printf("*retval = %d\n",*retval);
  29.         sleep(1);
  30.         return 0;
  31.        
  32.        
  33.         return 0;
  34. }
复制代码

练习4:多线程拷贝文件(缺陷当文件过大,会导致偏移量出错)

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <fcntl.h>
  4. #include <errno.h>
  5. #include <sys/stat.h>
  6. #include <unistd.h>
  7. typedef struct
  8. {
  9.         int fd_s;
  10.         int fd_d;
  11.         int size;
  12.         int len;
  13.         int id;
  14. }msg_t;
  15. void * do_copy (void *arg)
  16. {
  17.         msg_t p = *(msg_t*)arg;
  18.         lseek(p.fd_s,p.size*p.id,SEEK_SET);
  19.         lseek(p.fd_d,p.size*p.id,SEEK_SET);
  20. //        printf("tid = %ld id = %d fd_s = %d fd_d = %d size = %d len = %d\n",pthread_self(),p.id,p.fd_s,p.fd_d,p.size,p.len);//调试代码
  21.         char buf[p.len];
  22.         int ret = read(p.fd_s,buf,p.len);
  23.         write(p.fd_d,buf,ret);
  24.         return NULL;
  25. }
  26. //cp src dest
  27. int main(int argc, const char *argv[])
  28. {
  29.         if (argc!=3)
  30.         {
  31.                 printf("Usage: %s <src> <dest>\n",argv[0]);
  32.                 return -1;
  33.         }
  34.        
  35.         int fd_s = open(argv[1],O_RDONLY);
  36.         int fd_d = open(argv[2],O_WRONLY|O_TRUNC|O_CREAT,0666);
  37.         if (fd_s < 0 || fd_d < 0)
  38.         {
  39.                 perror("open fail");
  40.                 return -1;
  41.         }
  42.         int n = 0;
  43.         printf("Input threads num: ");
  44.         scanf("%d",&n);
  45.         int i = 0;
  46.         int ret = 0;
  47.         pthread_t tid[n];
  48.        
  49.         msg_t msg[n];
  50.         struct stat st;
  51.         if (stat(argv[1],&st) < 0)
  52.         {
  53.                 perror("stat fail");
  54.                 return -1;
  55.         }
  56.         int f_len = st.st_size;
  57.        
  58.         for (i = 0; i < n; ++i)
  59.         {
  60.                 msg[i].fd_s = fd_s;
  61.                 msg[i].fd_d = fd_d;
  62.                 msg[i].size = f_len / n;
  63.                 msg[i].id = i;
  64.           
  65. #if 1
  66.                 if (i == n-1)
  67.                 {
  68.                         msg[i].len  = f_len - (f_len/n)*(n-1);
  69.                 }else
  70.                 {
  71.                         msg[i].len  = f_len/n;
  72.                 }
  73. #endif
  74.                 ret = pthread_create(&tid[i],NULL,do_copy,&msg[i]);
  75.                 if (ret != 0)
  76.                 {
  77.                         errno = ret;
  78.                         perror("pthread_create fail");
  79.                         return -1;
  80.                 }
  81.         }
  82.         printf("----main-----\n");
  83.         for (i = 0; i < n; ++i)
  84.                 pthread_join(tid[i],NULL);
  85.         close(fd_s);
  86.         close(fd_d);
  87.        
  88.         return 0;
  89. }
复制代码








免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张裕

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表