Linux-C-day-6-线程

线程基础

线程是进程的一个执行单元,执行一段程序片段,线程共享全局变量;线程的查看可以使用命令或者文件来进行查看;命令:ps -T -p <pid> -T:表示用于开启线程查看;top -H -p <pid> -H 用于开启线程查看;还可以通过htop 使用F2开启树状图查看选项,或者开启显示自定义线程名选项;F10用于退出设置;文件:/proc/PID/task 线程的名字默认和进程相同;/proc/PID/task/comm线程名称;

线程的相关函数
线程标识

pthread_t pthread_self(void);返回值是当前线程ID,可以使用%lu打印线程pid的值;设置线程名称:int prctl(int option,unsigned long arg2);PR_GET_NAME用于获得当前线程的名字,PR_SET_NAME,用于设置当前线程的名字;arg2:线程名称最大长度为15个字节,并且应该以'\0'结尾,一共16个字符;返回值:0表示成功非0表示出错;
thread01.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>

int main(){
    printf("PID:%d,TID:%lu\n",getpid(),pthread_self());
    char name[16] = {0};
    prctl(PR_SET_NAME,"test");
    prctl(PR_GET_NAME,name);
    printf("TNAME:%s\n",name);
}

thread02.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>

int info(){
    printf("PID:%d,TID:%lu",getpid(),pthread_self());
    char name[16] = {0};
    prctl(PR_GET_NAME,name);
    printf("TNAME:%s\n",name);
}

void* method(void* arg){
    info();
}

int main(){
    info();
    pthread_t tid;
    pthread_create(&tid,NULL,method,NULL);
    printf("new tid:%lu\n",tid);
    sleep(1);
}
线程创建

int pthread_create(pthread_t *tidp,pthread_attr_t *attr,void (start_rtn)(void),void *arg);tidp:线程ID指针;
 attr:线程属性:绑定:pthread_attr_setscope(pthread_attr_t *attr, int scope);scope:PTHREAD_SCOPE_PROCESS:表示不进行绑定,这个是默认的选项;PTHREAD_SCOPE_SYSTEM表示绑定轻进程(LWP)/内核线程;
 分离:pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);detachstate:PTHREAD_CREATE_DETACHED:表示分离线程;PTHREAD_CREATE_JOINABLE表示非分离线程;
 线程使用的是缺省的堆栈,线程的优先级和父进程同级别;
线程的ID指针返回值是void类型的指针函数;arg:表示的是start_rtn的形式参数;返回值0表示成功,非0表示出错;

线程消亡

线程的消亡分为正常终止和线程取消两种情况;线程正常终止:对于子线程来说线程处理函数return 返回一个返回值,线程可以终止,线程终止还可以调用pthread_exit(void* retval);来进行处理,retval:表示函数的返回指针,只要pthread_join中的第二个参数retval不是NULL,这个值江北传递给retval,如果用在线程回调函数中,将返回线程数据;

对于主线程:线程合并:int pthread_join(pthread_t tid, void **retval);tid:被等待的线程标识符,retval一个用户定义的指针,它可以用来存储被等待线程的返回值,返回值:0表示成功,非0表示错误码;当进行线程合并时,可以由其他线程来终止,并且回收资源;
pthread_join.c:线程取消函数

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>

int info(){
    printf("PID:%d,TID:%lu",getpid(),pthread_self());
    char name[16] = {0};
    prctl(PR_GET_NAME,name);
    printf("TNAME:%s\n",name);
}

void* method(void* arg){
    sleep(5);
    info();
}

int main(){
    info();
    pthread_t tid;
    pthread_create(&tid,NULL,method,NULL);
    printf("new tid:%lu\n",tid);
    //sleep(1);
    pthread_join(tid,NULL);
}

主线程终止的另一种方式是线程分离int pthread_detach(ppthread_t tid);tid:表示要释放线程的标识符,返回值0表示成功,非0表示错误码;但是这样是不能被其他线程终止的,存储在它终止时,有系统自动的回收释放;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

线程取消:取消点取消,如果线程接收到取消信号,到达指定位置才能能取消,取消点取消分为手动:void pthread_testcancle(void);自动:通过引起阻塞的系统调用来取消;
 线程取消:发送取消信号:int pthread_cancel(pthread_t pthread),需要注意的是:发送成功并不一定意味着thread线程就会终止,发送CANCEL指令后,使用pthread_join 函数,等待指定的线程完全退出以后,在继续执行,否则容易产生段错误;函数的返回值0表示成功,非0值表示失败;
 设置当前线程的取消类型:int pthread_setcancelstate(int state,int *oldstate);state:PTHREAD_CANCEL_ENABLE:线程取消启用;PTHREAD_CANCEL_DISABLE:表示线程取消禁用;oldstate:表示线程之前的状态;
 设置当前线程的取消类型:int pthread_setcanceltype(int type,int *oldtype);type:PTHREAD_CANCEL_DEFFERED:表示取消点取消;PTHREAD_CANCEL_ASYNCHRONOUS:表示立即退出;oldstate:表示之前的取消类型;
 设置取消点:void pthread_testcanel(void);
pthread_cancel.c

#include <stdio.h>
#include <pthread.h>

long count = 0;

void* default_func(void* arg){
    for(;;){
        pthread_testcancel();
        count++;
    }
}

void* disable_func(void* arg){
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    for(;;){
        pthread_testcancel();
        count++;
    }
}
void* force_func(void* arg){
    pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    for(;;){
        count++;
    }
}

void* disable_force_func(void* arg){
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    for(;;){
        count++;
    }
}
void* watch(void* arg){
    for(;;){
        sleep(1);
        printf("count:%d\n",count);
    }
}

void* cancel(void* arg){
    sleep(5);
    pthread_t* ptid = arg;
    printf("cancel %lu\n",ptid[0]);
    pthread_cancel(ptid[0]);    
}

int main(int argc,char* argv[]){
    typedef void* (*Func)(void*); 
    Func funcs[3] = {default_func,watch,cancel}; 

    int c;
    while((c=getopt(argc,argv,"dp"))!=-1){
        switch(c){
            case 'd':
                funcs[0] = (funcs[0] == default_func?disable_func:disable_force_func);
                break;
            case 'p':
                funcs[0] = (funcs[0] == default_func?force_func:disable_force_func);
                break;
        }
    }

    pthread_t tids[3];
    int i=0;
    for(;i<3;i++){
        pthread_create(&tids[i],NULL,funcs[i],&tids);
        printf("create thread %lu\n",tids[i]);
    }
    for(i=0;i<3;i++){
        pthread_join(tids[i],NULL);
    }

}

发送信号:int pthread_kill(pthread_t tid, int sig);tid:表示线程ID,sig:信号,0表示保留信号,用于测试线程是否存在,信号为正数表示系统自定义信号或者系统自定义信号;返回值:0表示调用成功,ESRCH:表示线程不存在;EINVAL:表示信号不合法;


设置线程并发度:并发度表示线程并发的数目,int pthread_setconcurrency(int level);获取线程并发度:int pthread_getconcurrency(void);
 void pthread_cleanup_push(void (routine)(void),void *arg);void pthread_cleanup_pop(int execute);execute通常为0;关于线程的信息可以man 7 threads;
 线程间通信使用全局变量的方式进行;

线程同步

信号量

信号量的创建:int sem_init(sem_t *sem,int pshared,unsigned int value);sem:表示信号量对象;pshared:表示信号量的类型,0表示线程共享,<0:表示进程共享;value:用于表示初始值;返回值0表示成功,-1表示失败;


Ã销毁:int sem_destory(sem_t *sem);sem:表示信号量对象;返回值0表示成功,-1表示失败;


等待:包括三种:阻塞等待,int sem_wait(sem_t *sem);sem:标识信号量对象;返回值0表示成功,-1表示失败;非阻塞等待:int sem_trywait(sem_t *sem);sem:表示信号量对象;返回值0表示成功,-1表示失败;超时等待:int sem_timedwait(sem_t *sem,struct timespec *abstime);sem:表示信号量对象;abstime:表示等待时间;-1表示等待超时,0表示成功;
̳触发:sem:信号量对象;0:表示成功;-1:表示成功;


互斥量

互斥量和信号量的区别:
 1、互斥量用于线程的互斥,信号用于线程的同步互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排他性,但是沪指无法限制资源访问者对其资源的访问顺序,也就是访问是无序的;同步:是指在互斥的基础上,通过其它机制实现访问者对资源的有序访问,在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况 都是互斥的,少数情况是指可以允许多个访问者同时访问资源;
 2、互斥量值只能是0或者1,信号量值可以作为非负整数,也就是说,一个互斥量只能用于一个资源的互斥访问,它不能解决多个资源的多线程互斥问题,信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量时,也可以完成一个资源的互斥访问;
 3、互斥量的加锁和解锁必须有同一个线程使用,信号量可以由一个线程释放,另一个线程得到;
 4、信号量可以允许多个线程进入临界区,互斥体只能允许一个线程进入临界区;
 ATM取款,toilet;互斥量分为静态分配互斥量,实现特点是简单,pthread_mutex = PTHREAD_MUTEX_INITIAVIZER;动态分配互斥量:特点是可以设置更多的选项;pthread_mutex_init(&mutex,NULL)或者pthread_mutex_destory(&mutex);
 互斥量的操作:加锁:互斥锁int pthread_mutex_lock(pthread_t *mutex);尝试加锁 int pthread_mutex_trylock(pthread_t *mutex),mutex:表示的也是互斥锁;解锁:int pthread_mutex_unlock(pthread_t *mutex),mutex同样表示的是互斥锁;
mutex01.c

#include <stdio.h>
#include <pthread.h>

void* func(void* arg){
    pthread_mutex_lock(arg);
    printf("enter func\n");
    sleep(1);
    printf("do something\n");
    sleep(1);
    printf("level func\n");
    pthread_mutex_unlock(arg);
}

int main(int argc,int argv[]){
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex,NULL);
    
    pthread_t tids[3];
    int i;
    for(i=0;i<3;i++){
        pthread_create(&tids[i],NULL,func,&mutex);
    }
    for(i=0;i<3;i++){
        pthread_join(tids[i],NULL);
    }
    pthread_mutex_destroy(&mutex);

}

mutex02.c

#include <stdio.h>
#include <pthread.h>

void* func(void* arg){
    pthread_cleanup_push(pthread_mutex_unlock,arg);

    pthread_mutex_lock(arg);
    printf("enter func\n");
    sleep(1);
    printf("do something\n");
    sleep(1);
    printf("level func\n");
    pthread_exit(0);

    pthread_cleanup_pop(0);
}

int main(int argc,int argv[]){
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex,NULL);
    
    pthread_t tids[3];
    int i;
    for(i=0;i<3;i++){
        pthread_create(&tids[i],NULL,func,&mutex);
    }
    for(i=0;i<3;i++){
        pthread_join(tids[i],NULL);
    }
    pthread_mutex_destroy(&mutex);

}
条件变量

线程挂起直到共享数据的某些条件得到满足;条件变量分为静态分配条件变量,pthread_cond_t_cond = PTHREAD_COND_INITIALIZER特点是比较简单;动态分配静态变量:特点是可以设置更多的选项,pthread_cond_init(&cond,NULL);或者使用pthread_cond_destory(&cond);
操作:
 条件等待:int pthrad_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);cond:表示条件变量;mutex:表示互斥锁;
 计时等待:int pthread_cond_timedwait(pthread_cond_t *cond,pthread_muex_t *mutex,const struct timespec *abstime);cond:表示条件变量;mutex:表示互斥锁;abstime:表示等待时间;返回值:ETIMEDOUT:表示超时等待结束;
激活操作:
 单个激活:int pthread_cond_signal(pthread_cond_t *cond);cond:表示条件变量,返回值0表示成功,正数表示错误码;
Ϳ全部激活: int pthread_cond_broadcast(pthread_cond_t *cond);cond:表示条件变量,返回值0表示成功,正数表示错误码;

线程读写锁

读取锁是共享锁,写入锁是独占锁;
 静态分配读写锁:pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER,特点是实现简单;动态分配读写锁:pthread_rwlock_init(&rwlock,NULL);pthread_rwlock_destory(&rwlock);可以用于设置更多选项

关于锁的操作

读取锁:加读取锁:int pthread_rwlock_rdlock(pthread_rwlock *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);

线程和进程的区别

接口对比:

getpid pthread_self 用于获得控制流的ID
fork pthread_create 创建新的控制流
exit pthread_exit 退出已有的控制流
waitpid pthread_join 等待控制流并获得结束代码

1、进程是资源分配和拥有的基本单位,线程是处理器调度的基本单位;
2、进程拥有独立的地址空间,线程共性进程的地址空间;
3、线程上下文切换比进程上下文切换要快得多;
4、子进程崩溃不会影响其它子进程,但是任何一个线程崩溃,整个程序都会崩溃;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容