同一个进程中,如何保持线程之间数据的私有性。线程之间如何交互。是这一章讨论的重点。
#include "../include/apue.h"
#include <pthread.h>
#include <string.h>
static void *func(void *p){
puts("Thread is working.\n");
pthread_exit(NULL);
}
int main(){
pthread_t tid;
int err,i;
pthread_attr_t attr;
pthread_attr_init(&attr);
// 修改每个线程的栈大小
pthread_attr_setstacksize(&attr,1024*1024);
for(i = 0;;i++){
err = pthread_create(&tid,&attr,func,NULL); // attr就是修改后的线程属性
if(err){
err_sys("pthread_create()");
break;
}
}
printf("i = %d\n",i);
pthread_attr_destroy(&attr);
exit(0);
}
通过修改线程的属性修改了为每个线程分配的栈空间大小,这样创建出来的线程数量与默认的不一样。 pthread_attr_init函数初始化,用完之后, pthread_attr_destory函数销毁。
线程的属性不仅可以设定线程的栈空间大小,还可以创建分离的线程等等。
与线程属性一样,使用之前要初始化(pthread_mutexattr_init),使用完毕要销毁。
pthread_mutexattr_getpshared,pthread_mutexattr_setpshared - get and set the process - shared attribute
#include <pthread.h>
int pthread_mutexattr_getpshared(const pthread_mutexattr_t * restrict attr, int *restrict pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared);
p是指process,这两个函数的作用就是设定线程的属性是否可以跨进程使用。跨进程使用?
clone,__clone2 - create a child process
#define __GNU_SOURCE
#include <sched.h>
int clone(int(*fn)(void *),void *child_stack,int flags,void *arg,.../* pid_t *ptid, struct user_desc *tls, pid_t *ctid */);
clone进程的flags如果设置了CLONE_FILES则父子进程共享文件描述符表,正常情况文件描述符表是线程之间共享的,因为多线程是运行在同一个进程的地址空间之内的。
虽然clone函数描述的是创建子进程,但是实际上,如果将flags属性设置得极端分离(各种资源都独享),相当于创建了一个独立的子进程。
如果flags属性设置得极端近似(各种资源都共享),则相当于创建了兄弟线程。所以 对于内核来讲并没有进程这个概念,只有线程的概念。你创建出来的到底是进程还是线程,并不影响内核进行调度。
如果需要创建一个"东西"与当前的线程既共享一部分资源,又独占一部分资源,就可以使用clone函数创建一个既不是线程也不是进程的东西。就是什么鬼东西,就是一个能让内核调度起来,按照程序调度的东西。
因而进程其实也就只是一个基本的调度资源的概念。能调度多少资源,不能调度多少资源,也是程序说了算。
四种互斥量,不同的互斥量在会遇到不同的情况时效果是不同的。
互斥量类型 | 没有解锁时重新加锁🔒 | 不占用时解锁🔓 | 在已解锁时解锁🔓 |
---|---|---|---|
PTHREAD_MUTEX_NORMAL(常规) | 死锁🔒 | 未定义 | 未定义 |
PTHREAD_MUTEX_ERRORCHECK(检错❌) | 返回错误🙅 | 返回错误🙅♂️ | 返回错误🙅 |
PTHREAD_MUTEX | RECURSIVE(递归) | 允许✅ | 返回错误❌ |
PTHREAD_MUTEX_DEFAULT(默认,我们平时使用的就是这个) | 未定义 | 未定义 | 未定义 |
- 没有解锁时重新加锁🔒:当前mutex已lock,再次lock
- 不占用时:他人锁定🔒由你解锁的情况
- 在已解锁时解锁:当前mutex已unlock,再次unlcok的情况
是存储和查询某个特定线程相关数据的一种机制。��每个线程可以访问它自己单独的数据副本,而不需要担心与其他线程的同步访问问题。其实为了保存自己的数据,用key来保存,防止线程访问
errno最初时全局,现在变成宏定义了。
允许取消两种
- 异步cancel: 内核的操作方式
- 推迟cancel: 推迟到取消点再响应取消操作。取消点就是一个函数,收到取消请求时取消点的代码不会执行
所以在POSIX定义中是存在取消点的。当请求遇到了取消点就可以取消线程 - pthread_setcancelstate,修改线程的可取消状态,可以将线程设置为可取消的或不可取消的
- pthread_setcanceltype,用来修改取消类型,可以选择异步还是推迟
- pthread_textcancel,人为方式取消点。
每个线程都持有一个mask位图和一个padding位图。每个进程都持有一个padding位图而没有mask位图
从内核态回到用户态之前,当前线程先用自己的mask位图与进程级别的padding按位与&运算,如果有信号就处理;然后再用自己的mask位图与自己的padding位图按位与运算,再处理相应的信号。
所以其实是哪个线程被调度,就由哪个线程相应进程级别的信号。
所以进程之间可以相互发信号
pthread_kill - send a signal to a thread
#include <signal.h>
int pthread_kill(pthread_t thread, int sig);
Compile and link with -pthread.
pthread_kill就是在线程阶段发信号,thread表示给哪个线程发信号,sig发送哪个信号
sigwait设置等待信号出现。
#include <signal.h>
int sigwait(const sigset_t *restrict set,int *restrict signop);
以下是主线程进行,其他线程在等待信号的例子。
#include "../include/apue.h"
#include <pthread.h>
int quitflag;
sigset_t mask;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t waitloc = PTHREAD_COND_INITIALIZER;
// 初始化互斥量的条件
void *thr_fn(void *arg){
int err,sig;
for(;;){
err = sigwait(&mask,&sig); // 等待一个或多个信号的到来 并初始化sig
if(err!=0)
err_exit(err,"sigwait failed");
switch (sig)// 判断信号的类型
{
case SIGINT:
printf("\ninterrupt\n");
/* code */
break;
case SIGQUIT:
pthread_mutex_lock(&lock);
quitflag = 1;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&waitloc);
// 发布得到信号的条件,通知其他线程
return (0);
default:
printf("unexpected signal %d\n",sig);
exit(1);
}
}
}
int main(void){
int err;
sigset_t oldmask;
pthread_t tid;
sigemptyset(&mask); // 添加信号集
sigaddset(&mask,SIGINT);
sigaddset(&mask,SIGQUIT);
if((err = pthread_sigmask(SIG_BLOCK,&mask,&oldmask))!=0) // 修改mask
err_exit(err,"SIG_BLOCK error");
err = pthread_create(&tid,NULL,thr_fn,0);
if(err!=0)
err_exit(err,"can't create thread");
pthread_mutex_lock(&lock);
while(quitflag == 0)
pthread_cond_wait(&waitloc,&lock); // 线程等待条件变量
pthread_mutex_unlock(&lock);
quitflag = 0;
if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0) // sigpromask 重置mask位图q
err_sys("SIG_SETMASK error");
exit(0);
}
pthread_sigmask人为干预线程级别的mask位图。类似sigsetmask函数
写时拷贝
pread和pwrite。