前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >我有一个问题,用了多线程后,两个问题有了现在

我有一个问题,用了多线程后,两个问题有了现在

作者头像
编程珠玑
发布2019-12-26 18:51:51
9790
发布2019-12-26 18:51:51
举报
文章被收录于专栏:编程珠玑编程珠玑

来源:公众号【编程珠玑】

作者:守望先生

ID:shouwangxiansheng

多线程,作为一个开发者,这个名词应该不陌生。我在《对进程和线程的一些总结》中也有介绍,这里就不详述。

为什么要用多线程

很显然,多线程能够同时执行多个任务。举个例子,你打开某视频播放器,点击下载某个视频,然后你发现这个时候一直在下载,其他啥都干不了,那你肯定骂*。所以在这种情况下,可以使用多线程,让下载任务继续,同时也能继续其他操作。

作为一个包工头,一堆砖要搬,但是就一个人,可是你只能搬这么多,怎么办?多找几个人一起搬呗,但是其他人就也需要付工钱,没关系,能早点干完也就行了,反正总体工钱差不多。

同样的,如果有一个任务特别耗时,而这个任务可以拆分为多个任务,那么就可以让每个线程去执行一个任务,这样任务就可以更快地完成了。

代价

听起来都很好,但是多线程是有代价的。由于它们“同时”进行任务,那么它们任务的有序性就很难保障,而且一旦任务相关,它们之间可能还会竞争某些公共资源,造成死锁等问题。

绑核

通过下面的命令可将进程proName程序绑在1核运行:

代码语言:javascript
复制
taskset -c 1 ./proName

而如果只绑定了一个核,那么同一时刻,只有一个线程在运行,而线程之间的切换又会消耗资源,那么这种情况下反而会导致性能降低。

另外一种情况,就是设置的线程数大于总的逻辑CPU数:

代码语言:javascript
复制
$ cat /proc/cpuinfo| grep "processor"| wc -l
8

这样的情况下,设置更多的线程并不会提高处理速度。

小结

优点:

  • 更快,加快处理任务
  • 更强,同时处理多任务

缺点:

  • 难控制,编程困难
  • 不当使用降低性能,线程切换
  • bug难定位,资源竞争

如何创建多线程

普通的进程通常只有一个线程,称为主线程。

创建线程需要使用下面的函数:

代码语言:javascript
复制
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

参数有必要做一下说明

  • thread 线程ID指针,创建成功时,会保存在此
  • attr 线程属性,控制线程的一些行为
  • start_routine 线程运行起始地址,是一个函数指针
  • arg 函数的参数,只有一个参数,因此多个参数需要打包在一起

创建成功时,返回0,否则出错。 看到了吗,到处都有void*的身影(参考《void*是什么玩意》)。

使用时注意包含头文件

代码语言:javascript
复制
#include <pthread.h>

,并且在链接时加上-lpthread,因此它不在libc库中。在《一个奇怪的链接问题》中提到,对于非glibc库中的库函数,都需要显式链接对应的库。

试着写一个简单的多线程程序,简单起见,我们暂时不设置任何属性,将attr字段设置为NULL:

代码语言:javascript
复制
//来源:公众号【编程珠玑】
//main.c
#include <stdio.h>
#include <pthread.h>
void *myThread(void *id)
{
    printf("thread run,value is %d\n",*(int*)id);
    //return NULL; 这种方式也可以退出线程
    pthread_exit((void*)0);//退出线程
}
int main(void)
{
    pthread_t tid ;
    int i = 10;
    int status = pthread_create(&tid,NULL,myThread,(void*)&i);
    if(status < 0 )
    {
        printf("crete failed\n");
    }
    printf("main func finished\n");
    return 0;
}

编译运行:

代码语言:javascript
复制
 $ gcc -o main main.c -lpthread
 $ ./main
 main func finished

发现运行的结果并不如我们预期那样,就好像线程没有执行一样。

原因在于,如果主线程退出了,那么其他线程也会退出。所谓,皮之不存,毛将焉附,所有线程都共同使用很多资源,相关内容也可以从《对进程和线程的一些总结》中了解到。 如何改进呢?我们可以等线程执行完啊,于是,在主线程退出前sleep:

代码语言:javascript
复制
int main(void)
{
    pthread_t tid ;
    int i = 10;
    int status = pthread_create(&tid,NULL,myThread,(void*)&i);
    if(status < 0 )
    {
        printf("crete failed\n");
    }
    printf("main func finished\n");
    sleep(1);
    return 0;
}

这样就好了(注意添加头文件#include <unistd.h>)。

代码语言:javascript
复制
main func finished
thread run,value is 10

但是你会发现,main func finished可能会先打印。这也就呼应了文章标题。 但是转念一想,如果线程执行的时间超过一秒呢,难道就要sleep更长时间吗?而很多时候甚至根本不知道线程要执行多长时间,那怎么办呢?

还可以使用:

代码语言:javascript
复制
int pthread_join(pthread_t thread, void **retval);

thread是前面获得的线程id,而retval包含了线程的返回信息,假设我们完全不关心线程的退出状态,那么可以设置为NULL。

修改代码如下:

代码语言:javascript
复制
int main(void)
{
    pthread_t tid ;
    int i = 10;
    int status = pthread_create(&tid,NULL,myThread,(void*)&i);
    if(status < 0 )
    {
        printf("crete failed\n");
    }
    printf("main func finished\n");
    pthread_join(tid,NULL);
    return 0;
}

这种情况同样可以达到目的,pthread_join,会阻塞程序,直到线程退出(前提是线程为非分离线程)。但是如果要等待多个线程呢?

线程终止

以下几种情况下,线程会终止

  • 线程函数返回
  • 调用pthread_exit,主线程调用无碍
  • 调用pthread_cancel
  • 调用exit,或者主线程退出,所有线程终止

注意

假如修改下面的代码:

代码语言:javascript
复制
int main(void)
{
    pthread_t tid;
    int i = 10;
    int status = pthread_create(&tid,NULL,myThread,(void*)&i);
    if(status < 0 )
    {
        printf("crete failed\n");
    }
    i = 6;
    printf("main func finished\n");
    pthread_join(tid,NULL);
    return 0;
}

在创建线程后,修改i的值,你会发现在线程中打印的不会是10,而是6。

也就是说,创建线程的时候,传入的参数必须确保其使用这个参数时,参数没有被修改,否则的话,拿到的将是错误的值,

总结

本文通过一些小例子,简单介绍了线程概念,对于绑核,多线程同步等问题均一笔带过,将在后面的文章中继续介绍。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程珠玑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要用多线程
  • 代价
  • 绑核
  • 小结
  • 如何创建多线程
  • 线程终止
  • 注意
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档