• Pthread 用法笔记


    什么是线程?

    从技术上讲,一个线程被定义为一个独立的指令流。

    一个进程可以包含一个或多个线程。

    线程操作包括线程创建,终止,同步(连接,阻塞),调度,数据管理和进程交互。

    进程内的所有线程共享:

    • 相同的地址空间
    • 信号
    • 文件描述符
    • 工作目录
    • 用户和组 ID

    每个线程具有单独的:

    • 堆栈指针
    • 寄存器
    • 调度属性(如策略或优先级)
    • 线程特定的数据

    线程的优点:

    1. 上下文切换的开销减小,提高了效率。
    2. 共享存储器,方便构造并发服务器。

    缺点:

    1. 同时访问同一个变量的冲突。
    2. 缺乏健壮性,一个线程故障可能就需要终止整个进程。

    什么是 Pthreads?

    POSIX 线程库是 C/C++ 的基于标准的线程API。

    利用它我们可以操作线程,开发并行处理的程序。

    线程创建和终止

    一个简单的例子:

    #include <pthread.h>
    #include <stdio.h>
    #define THRDS 5
    
    void *PrintHello(void *t) {
        printf("Hello World! It's me, thread #%ld!
    ", (long)t);
        pthread_exit(NULL);
    }
    
    int main () {
        pthread_t callThd[THRDS];
        for(long t=0; t<THRDS; t++){
            int rc = pthread_create(&callThd[t], NULL, PrintHello, (void *)t);
            if (rc){
                printf("ERROR: pthread_create() return %d
    ", rc);
                return -1;
            }
        }
        pthread_exit(NULL);
     }
    

    上面我们用到了 pthread_create 来创建线程。

    int pthread_create(pthread_t *thread, // 线程 ID
                       const pthread_attr_t *attr, // 线程属性,NULL 则采用默认属性
                       void *(* start_routine)(void *), // 要线程化的函数的指针
                       void *arg); // 传递给 start_routine 函数的参数
    

    线程函数的参数必须通过引用传递并转换为(void *)

    若要传递多个参数,可创建一个包含所有参数的结构体,再传递指向该结构体的指针。

    如果传递的参数是一个变量的地址,由于这是共享内存空间,变量对所有线程可见,很有可能在新线程访问它之前,此内存位置的值发生了更改。

    终止一个线程有下面几种方法:

    • 线程正常执行完后返回。
    • 线程调用 pthread_exit
    • 线程被另一个线程通过 pthread_cancel 取消。
    • 整个进程因调用 exec()exit() 而终止。
    • main() 先完成,且没有显式调用 pthread_exit

    如果没有显式地调用 pthread_exit()main() 就会在它产生的线程之前完成,那么所有线程都将终止。

    显示调用 pthread_exit(),则main() 会在结束前等待所有线程执行完毕。

    我们也可以在 main() 中调用 pthread_join(t, NULL); 来连接子线程,连接后,当前线程就会阻塞并等待子线程 t 的结束。

    另外创建时线程时可以通过线程属性指定是否可被连接。

    线程协调和同步

    Unix 的常见的线程同步机制:互斥(mutex)、信号量(semaphore)和条件变量(condition variable)。

    pthread 库提供的三种同步机制:

    • 互斥锁:阻止其他线程访问变量。
    • 连接(join):让一个线程等待,直到其他人终止。(上面已经提到)
    • 条件变量:数据类型 pthread_cond_t

    互斥

    Mutex是“互斥”(mutual exclusion)的缩写。

    一个简单的例子:

    #include <pthread.h>
    #include <stdio.h>
    #define THRDS 5
    pthread_t callThd[THRDS];
    pthread_mutex_t mutexsum;
    long sum;
    
    void *add(void*) {
        pthread_mutex_lock(&mutexsum);
        sum++;
        pthread_mutex_unlock(&mutexsum);
        return 0;
    }
    int main(int argc, char *argv[]) {
        pthread_mutex_init(&mutexsum, NULL);
    
        for(int i = 0; i < THRDS; i++) {
            pthread_create(&callThd[i], NULL, add, NULL);
        }
    
        for(int i = 0; i < THRDS; i++) {
            pthread_join(callThd[i], NULL);
        }
    
        printf("Sum =  %ld 
    ", sum);
        pthread_mutex_destroy(&mutexsum);
        return 0;
    }
    

    互斥变量必须声明为pthread_mutex_t类型,并且必须在可以使用它们之前进行初始化。有两种方法来初始化互斥变量:

    • pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
    • 使用pthread_mutex_init()。该方法允许设置互斥对象属性 attr。

    互斥变量最初是未上锁的。

    条件变量

    一个条件变量总是与一个互斥锁一起使用。

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #define THRDS  3
    #define TCOUNT 10
    #define COUNT_LIMIT 12
    
    int count = 0;
    pthread_mutex_t count_mutex;
    pthread_cond_t count_threshold_cv;
    
    void *inc_count(void *t) {
        for (int i = 0; i < TCOUNT; i++) {
            pthread_mutex_lock(&count_mutex);
    
            /* 检查是否数量达到阈值 */
            if (++count == COUNT_LIMIT) {
                printf("inc_count(): 线程 %ld, count = %d  达到阈值. ", (long)t, count);
                /* pthread_cond_signal 用来唤醒正在等待对应条件变量的线程,此时互斥锁必须是锁住的。
                  执行之后必须用 pthread_mutex_unlock 解锁互斥锁。
                  若有多个线程在等待条件变量,那么必须用 pthread_cond_broadcast 代替 pthread_cond_signal。
                  必须在调用 pthread_cond_signal 之前调用 pthread_cond_wait。
                  */
                pthread_cond_signal(&count_threshold_cv);
            }
    
            printf("inc_count(): 线程 %ld, count = %d 
    ", (long)t, count);
            pthread_mutex_unlock(&count_mutex);
            /* 稍微等待一会儿 */
            sleep(1);
        }
    
        pthread_exit(NULL);
    }
    
    void *watch_count(void *t) {
        printf("启动 watch_count(): 线程 %ld
    ", (long)t);
        pthread_mutex_lock(&count_mutex);
    
        while (count < COUNT_LIMIT) {
            printf("watch_count(): 线程 %ld count= %d. 继续等待...
    ", (long)t, count);
            /* pthread_cond_wait 总是自动且原子地解锁互斥锁。*/
            pthread_cond_wait(&count_threshold_cv, &count_mutex);
        }
    
        count += 125;
        printf("watch_count(): 线程 %ld count = %d.
    ", (long)t, count);
        pthread_mutex_unlock(&count_mutex);
        pthread_exit(NULL);
    }
    
    int main(int argc, char *argv[]) {
        int i, rc;
        pthread_t th[THRDS];
        pthread_attr_t attr;
        /* 初始化互斥锁和条件变量 */
        pthread_mutex_init(&count_mutex, NULL);
        pthread_cond_init (&count_threshold_cv, NULL);
        /* 为了兼容性,使用属性指明线程可被连接 */
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
        pthread_create(&th[0], &attr, watch_count, (void *)1l);
    
        for(long i = 1; i < 3; i++) {
            pthread_create(&th[i], &attr, inc_count, (void *)i);
        }
    
        /* 等待所有线程结束 */
        for (i = 0; i < THRDS; i++) {
            pthread_join(th[i], NULL);
        }
    
        printf ("main():  等待并且连接了 %d 个线程. 最终 count = %d.
    ", THRDS, count);
        /* 清理并退出 */
        pthread_attr_destroy(&attr);
        pthread_mutex_destroy(&count_mutex);
        pthread_cond_destroy(&count_threshold_cv);
        pthread_exit (NULL);
    }
    
    

    参考1. POSIX thread (pthread) libraries
    参考2. POSIX Threads Programming

  • 相关阅读:
    循环选择判断文件类型
    SpringBoot+MyBatis+Mysql+Durid实现动态多数据源
    Spring 常用注解
    Spring IOC容器中那些鲜为人知的细节
    java8 Stream对List<Map>的分组合并操作
    Java8的CompletableFuture 使用详解
    Spring MVC源码分析
    Spring AOP源码分析
    Spring中AOP必须明白的几个概念
    UriComponentsBuilder和UriComponents url编码
  • 原文地址:https://www.cnblogs.com/flipped/p/8674256.html
Copyright © 2020-2023  润新知