Linux 多线程 编程

在linux中是没有线程这一说的 线程是其他第三方库来帮助我们实现的

/proc/ 此文件夹是存放 所有运行程序的 执行空间文件夹

而 线程的 执行空间是在 父进程的 执行空间文件夹中

pmap -x 1110 查看 /proc/中的进程 的进程信息

从用户空间资源

  • 线程不代表资源,是程序的最小执行单元———线程与线程间只是栈不同
  • 进程代表资源,是程序调度的最小单元————进程与进程是全复制
    从内核空间资源
  • Linux应用层中,进程和线程是一样的
  • 线程的管理,线程与进程的区别
  • 线程的同步与互斥
  • 线程的信号处理
  • 线程的属性

线程的管理

linux中使用第三方的库进行多线程实现,-lpthread
gcc -g -o xxxx xxx.c -lpthread

pthread_self() 获得线程的tid
SYS_gettid 获得线程的tpid

线程创建

pthread_create()

  1. #include <pthread.h>
  2. int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
  3. pthread_t *thread : 线程的id 线程在进程中容器里的编号
  4. pthread_attr_t *attr 设置线程属性 NULL
  5. void *(*start_routine) (void *) 线程执行函数
  6. void *arg 传递给线程的参数

线程退出与等待

pthread_exit() 退出线程

  1. void pthread_exit(void *retval);
  • 自行调用pthread_exit
  • 其他线程调用 pthread_cancel
  • 线程执行结束
  • 创建线程的进程退出
  • 其中一个线程执行了exec类函数,因为会替换当前进程所有的地址空间子线程退出仅释放自己私有空间,私有栈空间

pthread_join() 等待进程退出

  1. int pthread_join(pthread_t thread, void **retval);

pthread_cleanup_push() 压入线程退出调用函数

pthread_cleanup_pop() 执行 压入线程退出函数

  1. #include <pthread.h>
  2. void pthread_cleanup_push(void (*routine)(void *),void *arg);
  3. void pthread_cleanup_pop(int execute);

线程取消

线程能否被取消要看两点:

  • 线程是否具有可取消属性—-默认可以被取消
  • 线程如果设置为到可取消点才能被取消时,线程不会被立刻取消

pthread_setcancelstate() 设置线程取消属性

  1. int pthread_setcancelstate(int state, int *oldstate);
  2. state
  3. PTHREAD_CANCEL_ENABLE 可取消属性
  4. PTHREAD_CANCEL_DISABLE 不可取消属性
  5. PTHREAD_CANCEL_ENABLE 位决取消
  6. oldstate
  7. 返回之前的属性

pthread_setcanceltype() 设置线程取消类型

  1. int pthread_setcanceltype(int type, int *oldtype);
  2. type:
  3. PTHREAD_CANCEL_ASYNCHRONOUS 立刻被取消
  4. PTHREAD_CANCEL_DEFERRED 只有到达一定取消点,才会取消
  5. // 默认 PTHREAD_CANCEL_DEFERRED

线程的私有数据

TSD私有数据,同名但是不同内存地址的私有数据结构

  • 创建
    pthread_key_create() 创建 私有变量
    pthread_key_delete() 删除 私有变量
  1. #include <pthread.h>
  2. int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
  3. // 参数2 : 退出调用函数
  4. int pthread_key_delete(pthread_key_t key);
  • 读写
    pthread_setspecific() 设置私有变量
    pthread_getspecific() 获取私有变量的值
  1. void *pthread_getspecific(pthread_key_t key);
  2. int pthread_setspecific(pthread_key_t key, const void *value);

例:

  1. pthread_key_t key;
  2. pthread_key_create(&key,NULL); //创建私有变量
  3. int len = 20;
  4. pthread_setspecific(key,&len); // 设置值
  5. int siz = *(int*)pthread_getspecific(key);//获取值
  6. pthread_key_delete(key); //删除私有变量

线程互斥

互斥锁通信机制

锁只是一直外加物质,而非在 数据上加锁 只是如同门一样

  • 对象 pthread_mutex_t
  • 初始化
    pthread_mutex_init()
    1. int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
    2. 参数1pthread_mutex_t 类型的值
    3. 参数2attr为锁属性,NULL值为默认属性
  • 上锁
    pthread_mutex_lock() 如果已经上锁就等待
    pthread_mutex_trylock() 不管是否上锁成功都返回
    1. 参数1pthread_mutex_t
    2. pthread_mutex_trylock 函数返回值:
    3. 函数成功返回0。任何其他返回值都表示错误
  • 解锁
    pthread_mutex_unlock()
  • 例:
    1. pthread_mutex_t key;
    2. pthread_mutex_init(&key,NULL);
    3. int a = 10;
    4. pthread_mutex_lock(&key); //上锁
    5. a++;
    6. pthread_mutex_unlock(&key); //解锁

条件锁

  • 条件变量 不能单独使用 必须配合互斥锁 一起使用
  • 对象:pthread_cond_t
  • 初始化方法:
    • 使用 pthread_cond_init 初始化
    • 直接赋值 PTHREAD_COND_INITIALIZER
  1. #include <pthread.h>
  2. int pthread_cond_destroy(pthread_cond_t *cond);
  3. int pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t * attr);
  4. // 直接赋值 初始化
  5. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • 等待条件变量
    • 是阻塞的过程

pthread_cond_timedwait() 可以设置超时时间
pthread_cond_wait()

  1. int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);//超时返回为 0
  2. int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrictmutex);
  • 通知条件变量
    • pthread_cond_signal 单个条件进行触发
    • pthread_cond_broadcast 触发全条件
  1. 通知条件变量
  2. #include <pthread.h>
  3. int pthread_cond_broadcast(pthread_cond_t *cond);
  4. int pthread_cond_signal(pthread_cond_t *cond);

读写锁

  • 如果当前线程读数据 则允许其他线程进行读操作 但不允许写操作
  • 如果当前线程写数据 则其他线程的读写都不允许操作
  • 对象 pthread_rwlock_t
  • 初始化 pthread_rwlock_init()
  1. int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
  2. restrict 对象;
  3. restrict 属性 默认为NULL就行
  • 删除 pthread_rwlock_destroy()
  1. int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
  2. restrict 要删除对象;
  • 读锁 pthread_rwlock_rdlock()
  1. int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
  2. int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
  • 写锁 pthread_rwlock_wrlock()
  1. int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
  2. int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
  • 解锁 pthread_rwlock_unlock()

无论是 读锁还是 写锁都使用此函数

  1. int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

线程信号

  • 每个线程可以向其他线程发送信号 pthread_kill
  • 每个信号都有信号屏蔽集合
  • 同进程下所有线程共享对某信号的处理方法
  • 发送信号 必须知道对方的 pid
  • 线程发送信号函数 pthread_kill()
  1. int pthread_kill(pthread_t thread, int sig);
  2. sig -->0 向某个线程调用pthread_kill,并且第二个参数为0
  3. pthread_kill(2,0); // 可以判断2号进程是否存在
  • 设置信号屏蔽集 pthread_sigmask()
  1. int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
  2. SIG_BLOCK 阻塞
  3. SIG_UNBLOCK 非阻塞
  4. SIG_SETMASK 屏蔽集合

不论给进程中的哪个线程发送SIG_KILL SIG_STOP 则当前进程中的所有线程都会推出

线程属性

  • 属性对象:
  1. typedef struct __pthread_attr_s
  2. {
  3. int __detachstate; //设置可取消属性
  4. int __schedpolicy; //调度策略
  5. struct __sched_param __schedparam;
  6. int __inheritsched;
  7. int __scope;
  8. size_t __guardsize;
  9. int __stackaddr_set;
  10. void *__stackaddr;
  11. size_t __stacksize;表示堆栈的大小。
  12. }pthread_attr_t;
  • 设置属性函数:pthread_attr_setdetachstate
  1. int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
  1. // 设置线程是否可等待退出
  2. struct __pthread_attr_s attr;
  3. pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
  4. pthrea_create(tid,&attr,func,argv);
  5. //PTHREAD_CREATE_DETACHED--->不能被等待
  6. //PTHREAD_CREATE_JOINABLE--->可以被等待
  7. // 默认是可等待退出
  • 获取属性函数:pthread_attr_getdetachstate
  1. int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);