Linux 多线程编程(一)2019-08-05

\color{blue}{多线程}

  1. 线程是程序中完成一个独立任务的完整执行序列,即一个可以调度的实体;进程是资源分配和调度的一个独立单位,线程是进程的一个实体,是CPU调度和分配的基本单位.
  2. 一个进程至少拥有一个线程,在同一个进程中的多个线程的内存资源是共享的.由于共享地址空间,所以多线程之间没有通信的必要,但必须要做好数据的同步和互斥.
  3. 并发是指在一个时间段内,多个任务交替进行,虽然看起来像是同时执行,但其实是交替的.

线程控制函数

1.\color{blue}{创建线程}

#include <pthread.h>
int pthread_create(pthread_t* thread,const pthread_attr_t* attr,void*(*start_routine)(void*),void* arg);
//成功时返回值为0,失败则返回错误码

attr参数可以用于设置新线程的属性,给它传NULL,表示使用默认线程属性.start_routine和arg参数分别指定新线程将运行的函数及其参数.
\color{blue}{实例}

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<pthread.h>

void* myfunc(void* arg)
{
   //打印子线程的ID
   printf("child thread ID= %ld\n",pthread_self());
   return 0;
}
int main(int argc,const char* argv[])
{
  //创建一个子线程
  //线程ID变量
  pthread_t pthid;
  //返回错误号
  int ret=pthread_create(&pthid,NULL,myfunc,NULL);
  if (ret!=0)
  {
    printf("error number:%d\n",ret);
    //打印错误信息
    printf("%s\n",sterror(ret));
  }
    
  printf("parent thread ID= %ld\n",pthread_self());
  for(int i=0;i<5;i++)
  {
     printf("I=%d\n",i);
  }
  sleep(2);
  return 0;
}

运行结果:

parent thread ID= 140594076149504
I=0
I=1
I=2
child thread ID= 140594068010752
I=3
I=4

2.\color{blue}{线程退出}

 #include <pthread.h>
 void pthread_exit(void *retval);
    // 线程终止

pthread_exit可以通过retval参数向线程的回收者传递其退出的信息.

#include<pthread.h>
int pthread_join(pthread_t thread,void*retval);

pthread_join()函数,以阻塞的方式等待thread指定的线程结束.当函数返回时,被等待线程的资源被收回.thread参数是目标线程的标识符,retval参数则是目标线程返回的退出信息,该函数会一直阻塞,直到被回收的线程结束为止,函数成功时返回值为0,失败则返回错误码.
\color{blue}{实例}

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<pthread.h>
int num = 100; //堆内存
void *myfunc(void *arg) {
    //打印子线程的ID
    printf("child thread ID= %ld\n", pthread_self());
    for (int i = 0; i < 5; i++) {
        printf("child I=%d\n", i);
        if (i == 2) {
            //退出携带信息
            pthread_exit(&num);
        }
    }
}
int main(int argc, const char *argv[]) {
    //创建一个子线程
    //线程ID变量
    pthread_t pthid;
    //返回错误号
     int ret = pthread_create(&pthid, NULL, myfunc, NULL);
    if (ret != 0) {
        printf("error number:%d\n", ret);
        //打印错误信息
        printf("%s\n", strerror(ret));
    }
    printf("parent thread ID= %ld\n", pthread_self());
    //阻塞等待子线程的退出,并且回收pcb
    void *ptr = NULL;
    pthread_join(pthid, &ptr);
    printf("number=%d\n", *(int *) ptr);
    for (int i = 0; i < 5; i++) {
        printf("parent I=%d\n", i);
    }
return 0;
}

运行结果:

parent thread ID= 140099126167296
child thread ID= 140099118028544
child I=0
child I=1
child I=2
number=100
parent I=0
parent I=1
parent I=2
parent I=3
parent I=4

3.\color{blue}{线程取消}
有时我们想要异常终止一个线程,即取消线程.

#include <pthread.h>
int pthread_cancel(pthread_t thread);
//成功时返回值为0,失败则返回错误码

不过,接收到取消请求的目标线程可以决定是否允许被取消以及如何取消,这分别由以下两个函数来控制:

#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
int pthread_setcanceltype(int type, int *oldstate);

4.\color{blue}{线程属性设置}
常用的属性:我们一般想要线程在结束时,自动释放其线程资源,自动回收pcb,则就需要给线程设置属性为detach,即线程分离.设置完之后就不需要调用pthread_join再进行线程回收了.
常用的函数与参数:
函数原型: int pthread_detach(pthread_t pthid);
设置属性类型:pthread_attr_t attr;
对线程属性变量初始化:int pthread_attr_init(pthread_attr_t* attr);
设置属性时通用做法:
代码上,可以这样表示:
pthread_t pthid;
pthread_attr_t attr; //线程属性
pthread_attr_init(&attr); //初始化线程属性
pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); //设置线程属性
pthread_create( &pthid, &attr,myfunc, NULL); //建立线程
\color{blue}{实例}

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<pthread.h>

void *myfunc(void *arg) {
    //打印子线程的ID
    printf("child thread ID= %ld\n", pthread_self());
    for (int i = 0; i < 5; i++) {
        printf("child I=%d\n", i);
        if (i == 2) {
            pthread_exit(NULL);
        }
    }
}
int main(int argc, const char *argv[]) {
    //创建一个子线程
    //线程ID变量
    pthread_t pthid;
    //返回错误号
    //初始化线程属性
    pthread_attr_t attr;
    pthread_attr_init(&attr);

/*  int pthread_attr_init(pthread_attr_t* attr);*/
    //设置分离
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    //创建线程时就设置线程分离
    int ret = pthread_create(&pthid, &attr, myfunc, NULL);
    if (ret != 0) {
        printf("error number:%d\n", ret);
        //打印错误信息
        printf("%s\n", strerror(ret));
    }
    printf("parent thread ID= %ld\n", pthread_self());
    //退出主线程,子线程不受影响
    //由于主线程退出,下面的不执行
    for (int i = 0; i < 5; i++) {
        printf("parent I=%d\n", i);
    }
    sleep(2);
    //释放资源
    pthread_attr_destroy(&attr);
    return 0;
}

运行结果:

parent thread ID= 139976127342336
parent I=0
child thread ID= 139976119203584
child I=0
child I=1
child I=2
parent I=1
parent I=2
parent I=3
parent I=4

5.\color{blue}{循环创建线程}

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<pthread.h>

void *myfunc(void *arg) {
    int num = arg;
    //打印子线程的ID
    printf("%dth child thread ID= %ld\n", num, pthread_self());
    return 0;
}
int main(int argc, const char *argv[]) {
    //创建一个子线程
    //线程ID变量
    pthread_t pthid[5];
    for (int i = 0; i < 5; i++) {  //第四个参数传递的是按值传递,可以对子线程标号,而传地址则不行,公用一个地址,注意此时不在为null了
/*     pthread_create(&pthid[i],NULL,myfunc,(void*)&i);*/
        pthread_create(&pthid[i], NULL, myfunc, (void *) i);
    }
    printf("parent thread ID= %ld\n", pthread_self());
    for (int i = 0; i < 5; i++) {
        printf("I=%d\n", i);
    }
    sleep(2);
    return 0;
}

运行结果:

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

推荐阅读更多精彩内容

  • 转自:Youtherhttps://www.cnblogs.com/youtherhome/archive/201...
    njukay阅读 1,605评论 0 52
  • 线程基础 线程是进程的一个执行单元,执行一段程序片段,线程共享全局变量;线程的查看可以使用命令或者文件来进行查看;...
    秋风弄影阅读 734评论 0 0
  • 概述 线程和进程本质上来说都属于一个内核调度单元,也就是说都可以作为一条单独的执行路径。但是多进程程序通常有一些限...
    loopppp阅读 455评论 0 0
  • 简介 线程创建 线程属性设置 线程参数传递 线程优先级 线程的数据处理 线程的分离状态 互斥锁 信号量 一 线程创...
    第八区阅读 8,549评论 1 6
  • Linux-创建进程与线程用到的函数解析 【1】exit: exit函数可以退出程序并将控制权返回给操作系统,而用...
    Yojiaku阅读 3,587评论 0 2