线程的故事

                                                   

                                               线程的故事

1. 什么是线程?

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

简单点理解:“进程对线程”相当于“老板对职员”,老板只有一个,职员有很多,老板可以同时叫多个职员完成一个项目的不同子任务,这就是多线程,每个子任务相当于一个线程;当然也可以不要职员,老板自己单干,那只有一个进程,老板一个一个任务去完成,最终也能完成,这个进程也可以称为主线程。

历史告诉我们分工有利于提高生产力,老板再强,面对一个比较大或复杂的项目,难免力不从心,这时团队的力量就体现出来了,老板负责组织协调工作,职员负责各个任务的具体完成,大家同步协作,共同推进,老板指挥指挥,沟通沟通,最终达到完成项目预定目标的任务。别觉得老板啥也不干,大多数老板啥都懂,但是要跳出那个环境才能环顾四方,通工易事。 

2. 什么时候用线程?

既然现在我们已经知道了线程的好处了,那我们什么时候用它?一句话——在你没空的时候,你就要想起线程了。比如你在打游戏,老妈叫你下楼吃饭,你去不去?去了坑队友,不去老妈马上就来关你电脑。这时要是有个分身帮你去吃个饭就好了,那好办呀,create一个thread去吃饭,你的主要任务还是保大哥。这个设定是不是太棒了!

3. 怎么用线程?

现实世界的职员你要去招募,代码里面的职员招募就简单了,create一个就完事了。

3.1 线程的建立

function:int pthread_create(pthread_t *tid, const pthread_attr_t *attr, (void*)(*start_rtn)(void*), void *arg);

param1:tid,线程id,pthread_t tid声明,传入tid的地址,实际是由pthread_create内部创建的一个唯一线程id号,用以标识线程;

param2:attr,用来设置线程属性,通常为NULL;(线程的故事——附1:线程建立函数参数const pthread_attr_t *attr深入分析

param3:start_rtn,线程运行函数的起始地址;

param4:运行函数的参数;(线程的故事——附2:线程建立函数参数void *arg深入分析

return:成功,返回0;出错,返回-1。

这个方法看似很简单呀,不就是这么使用的么,看我来show一段:

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

static int count = 0;

void myprint(void* number){
    int i=0;
    for (i=0; i < (int)(*((int*)number)); i++) { 
        count++; 
        printf("The thread method count is = %d\n",count); 
        sleep(1); 
    }
    return;
}

int main(void)
{
    int err = 0;
    int param = 10;
    pthread_t tid;
    err = pthread_create(&tid, NULL, (void *)&myprint, (void*)&param);
    if (err != 0){
        printf("can't create thread: %d\n", strerror(err));
        return EXIT_FAILURE;
    }
    printf("new thread id = %lu.\n", tid);
    pthread_join(tid, NULL);

    return EXIT_SUCCESS;
}

运行环境:ubuntu14.04,平台自带gcc编译器

运行结果: 

root@ubuntu:/home/jack# make
gcc -o test test.c -lpthread
root@ubuntu:/home/jack# ./test
new thread id = 3075615552.
The thread method count is = 1
The thread method count is = 2
The thread method count is = 3
The thread method count is = 4
The thread method count is = 5
The thread method count is = 6
The thread method count is = 7
The thread method count is = 8
The thread method count is = 9
The thread method count is = 10
root@ubuntu:/home/jack# 

测试程序下载地址:https://download.csdn.net/download/sleeping_sunshine/11579882

我真NB,我会用线程了,这是我当初第一次用线程的感受~

3.2 线程的等待

function:int pthread_join(pthread_t tid, void **retval);

param1:tid,线程id;

param2:retval,用户定义的指针,用来存储被等待线程的返回值;

return:线程连接的状态,0是成功,非0是失败。

pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的,线程建立默认都是joinable的。

简单点理解,就是进程或者说是主线程你不能先于线程结束,就相当于下班了你没回家,老板也不能回家,老板要等你的子任务的工作结果好进行下一步的安排。调用pthread_join()函数的主线程就要等待响应线程的结束好回收资源。

如果调用pthread_join()会产生什么结果?

调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,调用者就要等在pthread_join()调用处,他要等id为pthread_id的线程执行完,好来释放资源。

如果不调用pthread_join()会产生什么结果?

创建一个线程默认的状态是joinable,如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process(僵尸进程),即还有一部分资源没有被回收(退出状态码),所以创建线程者应该pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源。

看个实例:

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

static int count = 0;

void myprint(void* number){
    int i=0;
    for (i=0; i < (int)(*((int*)number)); i++) { 
        count++; 
        printf("The thread method count is = %d\n",count); 
        sleep(1); 
    }
    return;
}

int main(void)
{
    int err = 0;
    int param = 10;
    pthread_t tid;
    err = pthread_create(&tid, NULL, (void *)&myprint, (void*)&param);
    if (err != 0){
        printf("can't create thread: %d\n", strerror(err));
        return EXIT_FAILURE;
    }
    printf("new thread id = %lu.\n", tid);
    //pthread_join(tid, NULL);

    return EXIT_SUCCESS;
}

运行环境:ubuntu14.04,平台自带gcc编译器

运行结果:

root@ubuntu:/home/jack# make
gcc -o test test.c -lpthread
root@ubuntu:/home/jack# ./test
new thread id = 3075996480.
root@ubuntu:/home/jack# 

测试程序下载地址:https://download.csdn.net/download/sleeping_sunshine/11579923

提问:为什么会出现这种运行结果,线程里面的log都没有打印?

怎么解决这个阻塞问题?

在有些情况下我们并不希望主线程因为调用pthread_join而阻塞(因为还要继续处理之后的任务),这时可以在子线程中加入代码:

pthread_detach(pthread_self()),(线程一开始就加入这句)

或者父线程调用:pthread_detach(thread_id)(非阻塞,可立即返回,create线程后就加入这句),将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。

static int count = 0;

void myprint(void* number){
    pthread_detach(pthread_self());//万能语句
    for (int i=0; i < *number; i++) { 
        count++; 
        printf("The thread count is = %d\n",count); 
        sleep(1000); 
    }
    return;
}

int main(void)
{
    int err;
    pthread_t tid;
    err = pthread_create(&tid, NULL, myprint, NULL);
    if (err != 0){
        printf("can't create thread: %s\n", strerror(err));
        return EXIT_FAILURE;
    }
    //pthread_detach(tid);//create结束就加入这句,默认新建线程是joinable的
    //pthread_join(tid,NULL);//pthread_detach之后不能调用pthread_join
    printf("The main count is = %d\n",count); 
    printf("new thread id = %lu.", tid);

    return EXIT_SUCCESS;
}

3.3 线程的退出

function:void pthread_exit(void* retval);

param1:retval,指向某个对象的指针,可作为函数的返回代码。

线程的终止可以是调用了pthread_exit或者该线程的例程结束。也就是说,一个线程可以隐式的退出,也可以显式的调用pthread_exit函数来退出。

pthread_exit函数唯一的参数value_ptr是函数的返回代码,只要pthread_join中的第二个参数value_ptr不是NULL,这个值将被传递给value_ptr。

 

以上就是简单的线程入门知识点,下面将会介绍线程使用中比较深入的知识,欢迎大家一起学习哦~

线程的故事——附1:线程建立函数参数const pthread_attr_t *attr深入分析

线程的故事——附2:线程建立函数参数void *arg深入分析

线程的故事——附3:多线程同步分析

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值