zoukankan      html  css  js  c++  java
  • Linux多线程开发I

    1.线程是允许应用进程并发执行多个任务的一种机制。一个进程可以包含多个线程。
    • 同一个程序中多有的线程均会独立执行相同程序,且共享同一份全局内存区域,包括初始化数据段、未初始化数据段、堆内存段。
    • 进程是CPU分配资源的最小单位,线程是操作系统调度执行的最小单位。
    • 线程是轻量级的进程(LWP:Light Weight Process),在Linux环境下线程的本质仍然是进程。
    2.查看指定进程的LWP号。
    ps -Lf pid
    

     

    3.线程和进程的区别。
    • 进程间的信息难以共享。父子进程之间并未共享内存,因此必须采用一些进程间通信方式,在进程间进行信息交换。
    • 调用fork()创建进程的时间开销较高,即使是写时复制技术,仍然需要复制诸如内存页表和文件描述符表之类的多种进程属性。
    • 线程之间能够方便快速地共享信息。只需要将数据复制到共享(全局/堆)变量中即可。
    • 创建线程比进程快10倍之多。线程间共享虚拟地址空间,无需采用写时复制来复制内存,也无需复制页表。
     
    4.线程之间共享和非共享资源。
    • 共享资源
      • 进程ID和父进程ID
      • 进程组ID和会话ID
      • 用户ID和用户组ID
      • 文件描述符表
      • 信号处置
      • 文件系统相关的信息:文件权限掩码(umask)、当前工作目录
      • 虚拟地址空间(除栈、代码段.text)
    • 非共享资源
      • 线程ID
      • 信号掩码(阻塞信号集)
      • 线程特有数据
      • error变量
      • 实时调度策略和优先级
      • 栈,本地变量和函数的调用链接信息
     
    5.线程相关函数。编译连接需要加上参数 -pthread
    # 查看线程相关函数
    $ man pthread  (tab键显示所有相关list)
     
    #include <pthread.h>
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
        - 作用:创建一个子线程。
                - 一般情况下,main函数所在的线程称之为主线程(main线程),其余创建的线程成为子线程。
                - 程序中默认只有一个进程,调用fork()函数后有2个进程
                - 程序默认只有一个线程,调用pthread_create()之后有2个线程
        - thread: 传出参数,线程创建成功后,子线程的线程ID被写道该变量中
        - attr: 设置线程的属性,一般使用默认值,NULL
        - start_rountine: 函数指针,这个函数是子线程需要处理的逻辑代码
        - arg: 给第三个参数使用,传参
        - 返回值:成功返回0,失败返回错误号,通过 char* strerror(int  errnum); 获取错误号信息
     
    // 主线程退出时,不会影响其它正常运行的线程
    void pthread_exit(void *retval);
        - 作用:终止线程,在哪个线程中调用,就表示终止哪个线程
        - retval: 传递一个指针,作为一个返回值,可以在pthread_join()中获得
     
    pthread_t pthread_self(void)
        - 作用:获取当前线程的线程ID
     
    int pthread_equal(pthread_t t1, pthread_t t2);
        - 作用:比较两个线程ID是否相等
     
    // 任何线程都可以回收其他终止的线程的资源
    int pthread_join(pthread_t thread, void **retval);
        - 作用:和一个已经终止的线程进行连接,回收子线程的资源
            - 这个函数是阻塞的,调用一次只能回收一个子线程,一般在主线程中使用    
        - thread: 需要回收的子线程的ID
        - retval: 接受子线程退出时的返回值
        - 返回值:成功返回0,失败返回错误号
     
    int pthread_detach(pthread_t thread);
        - 作用:分离线程,被分离的线程在终止时,自动释放资源返回给系统
            - 不会阻塞父进程
            - 不能多次分离
            - 不能连接一个已经分离的线程
        - thread: 线程ID 
        - 返回值:成功返回0,失败返回错误号
     
    int pthread_cancel(pthread_t thread);
        - 作用:取消线程(让线程终止)
            - 取消某个进程,可以终止某个线程的运行,但并不是马上终止,而是当子线程执行到一个取消点,线程才会终止
            - 取消点:系统规定好的一些系统调用,可以粗略的理解为用户区到内核区的切换,这个位置称之为取消点
        - thread: 线程ID
        - 返回值:成功返回0,失败返回错误号
    

      

    6.线程属性。
    int pthread_attr_init(pthread_attr_t *attr);
        - 作用:初始化线程属性变量
     
    int pthread_attr_destory(pthread_attr_t *attr);
        - 作用:释放线程属性的资源
     
    int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
        - 作用:获取线程分离的状态属性
     
    int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
        - 作用:设置线程分离的状态属性
        - attr: 线程的属性,可以通过pthread_attr_init获得
        - detachstate: 分离状态,PTHREAD_CREATE_DETACHED:使用attr创建的线程将以分离状态创建;PTHREAD_CREATE_JOINABLE:使用attr创建的线程将以可连接状态创建(默认)
    

      

    7.线程同步。
    • 线程的优势是可以通过全局变量来共享信息。但是必须确保多个线程不会同时修改同一变量,或者某一线程不会读取正在由其他线程修改的变量。
    • 临界区是指访问某一共享资源的代码片段,并且这段代码的执行应为原子操作,即同时访问统一资源的其他线程不应中断该片段的执行。
    • 线程同步:当有一个线程对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作,而其他线程则处于等待状态。
     
     
    8.互斥量(互斥锁,mutex,mutual exclusion)用来确保同时仅有一个线程可以访问某项共享资源。
    • 两种状态:已锁定和未锁定。至多只有一个线程可以锁定该互斥量。
    • 一旦线程锁定互斥量,随即成为该互斥量的所有者,只有所有者才能给互斥量解锁。
    • 如果多个线程试图执行临界代码,其中一个线程抢到CPU资源,将锁定互斥量,然后访问临界代码,之后解锁互斥量,期间其他想要访问互斥量的线程将被阻塞。
     
    9.互斥量相关操作函数。
    pthread_mutex_t    互斥量类型
     
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
        - 作用:初始化互斥量
        - mutex: 需要初始化的互斥量变量
        - attr: 互斥量相关的属性,NULL;restrict:C语言修饰符,被修饰的指针不能由另外一个指针进行操作
     
    // 释放互斥锁后再使用互斥锁会造成未知结果    
    int pthread_mutex_destory(pthread_mutex_t *mutex);
         - 作用:释放互斥量的资源
     
    int pthread_mutex_lock(pthread_mutex_t *mutex);
         - 作用:加锁,阻塞的,如果一个线程加锁了,其他线程只能阻塞等待
     
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
         - 作用:尝试加锁,如果加锁失败,不会阻塞,会直接返回
     
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
         - 作用:解锁
    

      

    10.死锁。两个或两个以上的进程在执行过程中,因争夺共享资源而造成的一种互相等待的现象,若无外力作用,这些进程都无法推进下去。此时称系统处于死锁状态或系统产生了死锁。
    11.死锁产生的场景。
    • 忘记释放锁
    • 重复加锁
    • 多线程多锁,抢占锁资源
     
    12.读写锁。允许多个读出,只允许一个写入。
    • 如果有其他线程读数据,允许其他线程执行读操作,但不允许写操作。
    • 如果有其他线程写数据,其他线程都不允许读、写操作。
    • 写是独占的,写的优先级高。
     
    13.读写锁相关操作函数。
    int pthread_rwlock_t    读写锁类型
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restict attr);
     
    int pthread_rwlock_destory(pthread_rwlock_t *rwlock);
     
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
     
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
     
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
     
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
     
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    

      

    14.生产者和消费者模型。
     
    15.条件变量。用于阻塞线程。
    • 条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用来实现线程同步。
    • 条件变量使线程睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:
      • 一个线程阻塞等待"条件变量的条件成立";  pthread_cond_wait
      • 另一个线程使"条件成立"(给出条件成立信号);pthread_cond_signal
    • 条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。
    pthread_cond_t        条件变量类型
    int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr *restrict attr);
     
    int pthread_cond_destory(pthread_cond_t * cond);
     
    int pthread_cond_wait(pthread_cond_t *restrict cond, const pthread_mutex_t *restrict mutex);
        - 作用:阻塞函数等待条件变量,调用了该函数,线程会阻塞
     
    # 当这个函数调用阻塞的时候,会对互斥锁mutex进行解锁,当不阻塞时,继续向下执行前会对对互斥锁重新加锁
    int pthread_cond_timedwait(pthread_cond_t *restrict cond, const pthread_mutex_t *restrict mutex, const struct *restrict abstime);
        - 作用:等待多长时间,调用该函数,线程会阻塞,直到指定的时间结束
     
    int pthread_cond_signal(pthread_cond_t * cond);
        - 作用:唤醒1个或多个线程
     
    int pthread_cond_broadcast(pthread_cond_t * cond);
        - 作用:唤醒全部线程
    

      

    16.信号量。用于阻塞线程。
    sem_t            信号量的类型
    int sem_init(sem_t *sem, int pshared, unsigned int value);
        - 作用:初始化信号量
        - sem: 信号量变量的地址
        - pshared: 0 用在线程间,非0 用在进程间
        - value: 信号量的值
     
    int sem_destory(sem_t *sem);
        - 作用:销毁信号量
     
    int sem_wait(sem_t *sem);
        - 作用:对信号量加锁,如果信号量的值大于0,则对信号量的值-1并立即返回;如果为0就阻塞,直到信号量的值可以减少或其他信号处理器终止了该函数的调用
        - 返回值:成功返回0,失败返回-1并设置errno    
     
    int sem_trywait(sem_t *sem);
     
    int sem_timedwait(sem_t *sem, const struct timespec * abs_timeout);
     
    int sem_post(sem_t *sem);
        - 作用:对信号量解锁,对信号量的值+1;如果信号量的值因此大于零,那么在sem_wait调用中被阻塞的另一个线程将被唤醒,并继续锁定信号量
        - 返回值:成功返回0,失败返回-1并设置errno
     
    int sem_getvalue(sem_t *sem, int *sval);
     
    

      

     
     
     
     
     
     
  • 相关阅读:
    操作系统原理5——文件管理
    Hadoop worldcount
    Java Future源码分析
    HBase入门教程
    Java自定义cas操作
    dubbo客户端源码分析(一)
    log4j2配置文件
    paxos协议
    Guava限流工具RateLimiter使用
    jvm内置锁synchronized不能被中断
  • 原文地址:https://www.cnblogs.com/tristatl/p/15120590.html
Copyright © 2011-2022 走看看