您的位置:首页 > 理论基础 > 计算机网络

网络编程(34)—— linux中的多线程

2016-12-12 08:49 197 查看
        本文主要研究下linux中多线程的用法。

        在之前的几篇文章中,介绍过Linux中多进程的实现,现在我们开始研究下linux中的多线程。之前曾经实现过多进程的linux服务器,解决了多客户端连接时的效率问题,但是它还存在以下问题:

1、创建子进程时,需要完全复制父进程的内存,造成很大的性能开销。

2、由于各个进程之间不属于同一片内存,进程间通信需要特殊的IPC技术

3、CPU在执行进程的代码时,需要进行上下文切换(将原进程的上下文数据从内存拷贝出去备份,然后拷贝进新进程的上下文到内存),而且这种切换的频率很高,每秒钟上百次甚至上千次,造成了很大的性能开销。

        多线程的提出,就是为了最大限度的减少上述问题,进程可以理解为构成操作系统的程序执行流,而线程可以理解为构成进程的程序执行流,有了多线程:

1、创建子线程时不必完全的赋值进程的内存

2、线程间共享数据区和堆区,不必采用特殊的IPC技术进行线程间的通信

3、最主要一点,线程间进行在切换时不需要进行上下文的切换。

        下面,给出linux中创建线程的函数原型:
#include <pthread.h>
int pthread_create(pthread_t * restrict thread, const pthread_attr_t * restrict attr,
void *(*start_routine) (void *), void * restrict arg);


thread -- 用来保存创建的进程的进程ID。

attr -- 用来设置进程的属性,通常情况下传入NULL即可

start_routine -- 线程主函数的函数指针,每个线程都有自己的main函数,这里的start_routine就是线程main函数的函数指针。

arg -- 传递给线程的参数的指针

        大家注意到了,在thread等参数的前面都有一个restrict关键字进行修饰,restrict的意思是告诉编译器,该指针或者引用所指的变量只能由该指针或者引用进行修改,无法通过其他方式进行修改。下面,我们先研究下restrict的用法和作用,首先先看下面的例子:

#include<stdio.h>
int main()
{
int i=0;
int* restrict pi=&i;
int* pi1 = &i;
*pi1 = 1;
printf("i=%d\n",i);
}


        这是我刚刚接触到restrict时,想要验证restrict功能的代码,以我最初的理解,restrict既然是告诉编译器,该指针指向的变量只能通过该指针进行修改,那么在代码中我通过pi1进行修改i的值时,编译器一定会报错,

        但是结果是不是这样呢,请看下面的运行过程(编译时需要加上 -std=c99的编译选项,因为从c99开始才支持对restrict的支持):
[Hyman@Hyman-PC retrictts]$ gcc restrictts.c -std=c99
[Hyman@Hyman-PC retrictts]$ ./a.out
i=1
        程序非但没有报错,反而得到了正确的运行结果?这是怎么回事?
        原来restrict只是针对编译器的一个优化策略,它告诉编译器,因为没有别的指针指向该变量,在编译阶段就计算出该变量的值然后在所有使用该变量的地方进行代码合并,比如有两段代码:

*pi+=2;

*pi+=5;

 将会被合并为

pi+=7

至于是不是针对只有该指针指向这个变量这个需要程序员自己去控制,就像这是一个规则,但是尊不遵守规则,那是你自己的事情了。

        简单介绍了下restrict关键字,我们继续进行多线程的讨论。先举例介绍多线程的使用方法:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void pthread_main(int* pmax)
{
int i=0;
for(i=0;i<*pmax;i++)
{
puts("child thread called ...");
sleep(1);
}
}
int main()
{
pthread_t ptid;
int i=10;
if(pthread_create(&ptid,NULL,(void*)pthread_main,(void*)&i) != 0)
{
puts("pthread_create() error");
exit(1);
}
sleep(11);
return 0;
}


第17行,我们利用pthread_create()函数创建了一个线程,进程函数为pthread_main,函数参数通过pthread_create中最后一个参数进行传递,在线程函数中打印字符串hild thread called ...,总的打印次数按照传过来的线程参数决定。

第22行,程序sleep了11秒,主要目的是为了等待子线程运行结束,给子线程的运行准备充足的时间。
运行结果如下(请注意编译时,需要添加-lpthread选项):

[Hyman@Hyman-PC retrictts]$ gcc restrictts.c -lpthread
[Hyman@Hyman-PC retrictts]$ ./a.out
child thread called ...
child thread called ...
child thread called ...
child thread called ...
child thread called ...
child thread called ...
child thread called ...
child thread called ...
child thread called ...
child thread called ...

Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
Git clone
git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL34
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: