线程基础篇

先介绍下用多线程和不用线程的区别

      典型的UNIX进程可以看作只有一个线程:那么这个进程在同一时间只能做一件事情,比如这个事情有四个任务ABCD,那么也只能串行的执行A->B->C->D,但是这会有一些不合理的地方,如果这四个任务是互相独立的(比如B是在界面上显示一个图片,C是执行下载的任务,而A的任务执行起来又有点慢)那么计算机会按顺序先花时间先执行好A,然后再去执行B做显示(响应的慢,半天出不来!),接着执行C去下载东西(说不定还会出现错误卡死整个进程)。

  如果想这四个互不相干的任务一起并行地执行呢,那就引出了线程概念。

  做法就是在程序设计时可以把进程设计成在同一时刻能够做不止一件事,每个线程处理各自独立的任务,这将会有如下好处:

1、每个线程在处理任务时可以采用同步编程的模式,比较简单。

2、这些线程可以访问该进程中相同的内存空间和文件描述符(即共享进程的资源),举个例子:在此进程的前面定义了一些变量或者缓存,在后面创建的线程都可以使用相同的这些变量或者缓存。再举个例子:我们使用迅雷下载的时候会将下载任务分为10个下载线程,这些线程都各自在下载并将获得的数据存到同一个文件中。

3、响应时间大大提升,例如在上面的例子中,开始进程后可以很快的看见B任务的显示或者C任务的下载,这样用户体验就好了。

4、效率高,前面那个没有创建线程的进程因为是顺序执行A->B->C->D的,若其中有任务在阻塞(即休眠),这时候CPU就空出来了,而如果分为了多个线程后CPU就可以去执行别的线程,避免了浪费。

线程一个形象比喻(摘自知乎--pansz用户)

桌子:进程

人:线程

菜:进程的资源

 

1、单进程单线程:一个人在一个桌子上吃菜。
2、单进程多线程:多个人在同一个桌子上一起吃菜。
3、多进程单线程:多个人每个人在自己的桌子上吃菜。

多线程的问题是多个人同时吃一道菜的时候容易发生争抢,例如两个人同时夹一个菜,一个人刚伸出筷子,结果伸到的时候已经被夹走菜了。。。此时就必须等一个人夹一口之后,在还给另外一个人夹菜,也就是说资源共享就会发生冲突争抢。

1、对于 Windows 系统来说,【开桌子】的开销很大,因此 Windows 鼓励大家在一个桌子上吃菜。因此 Windows 多线程学习重点是要大量面对资源争抢与同步方面的问题。
2、对于Linux 系统来说,【开桌子】的开销很小,因此 Linux 鼓励大家尽量每个人都开自己的桌子吃菜。这带来新的问题:坐在两张不同的桌子上,说话不方便。因此,Linux 下的学习重点大家要学习进程间通讯的方法。

开桌子的意思是指创建进程。开销这里主要指的是时间开销。可以做个实验:创建一个进程,在进程中往内存写若干数据,然后读出该数据,然后退出。此过程重复 1000 次,相当于创建/销毁进程 1000 次。在我机器上的测试结果是:
Ubuntu Linux:耗时 0.8 秒
Windows7:耗时 79.8 秒
两者开销大约相差一百倍。

这意味着,在 Windows 中,进程创建的开销不容忽视。换句话说就是,Windows编程中不建议你创建进程,如果你的程序架构需要大量创建进程,那么最好是切换到 Linux 系统。
大量创建进程的需求中,软件开发人员最好是避免使用 Windows。而服务器框架依靠大量创建进程来干活,甚至是对每个用户请求就创建一个进程(比如socket编程),这些服务器在Windows 下运行的效率就会很差。这"可能"也是放眼全世界范围,Linux 服务器远远多于 Windows 服务器的原因。

再次补充:如果你是写服务器端应用的,其实在现在的网络服务模型下,开桌子的开销是可以忽略不计的,因为现在一般流行的是按照CPU 核心数量开进程或者线程,开完之后在数量上一直保持,进程与线程内部使用协程或者异步通信来处理多个并发连接,因而开进程与开线程的开销可以忽略了。

另外一种新的开销被提上日程:核心切换开销。现代的体系,一般 CPU 会有多个核心,而多个核心可以同时运行多个不同的线程或者进程。当每个 CPU 核心运行一个进程的时候,由于每个进程的资源都独立,所以CPU 核心之间切换的时候无需考虑上下文。当每个 CPU 核心运行一个线程的时候,由于每个线程需要共享资源,所以这些资源必须从 CPU 的一个核心被复制到另外一个核心,才能继续运算,这占用了额外的开销。换句话说,在CPU 为多核的情况下,多线程在性能上不如多进程。因而,当前面向多核的服务器端编程中,需要习惯多进程而非多线程。

 

下面步入主题了:

 

线程标识:

  就像每个进程都有进程ID(pid_t类型)一样,每个线程也有一个线程ID(pthread_t类型)。区别就是:进程ID在整个系统中是位移的,但是线程ID值是在它所属的进程环境中有效。

 

线程操作系列函数如下:

//创建一个线程

1、int pthread_create(pthread_t *restrict thread,const pthread_attr_t* restrict attr,void *(*start_routine)(void *),void *restrict arg);

●参数1:创建后会将此线程的ID保存在此结构中;

●参数2:封装了线程的各种属性的属性对象,用来配置线程的运行,设为NULL则表示默认属性;

●参数3:是线程开始执行时调用的函数;返回的void指针将被pthread_join函数当做推出状态来处理;

●参数4:arg是作为参数传递给参数3的函数用的;

●返回值:成功返回0,错误返回EAGIN(超出线程数量)、EINVAL(atte参数无效)、EPERM(权限问题)

 

//获得自己的线程ID

2、pthread_t pthread_self(void);

 

//测试两个线程ID是否相等

3、int pthread_equal(pthread_t t1,pthread_t t2);

参数t1和t2是两个线程的ID,如果相等,就返回一个非零值,如果不相等则返回0;

 

//设置线程以释放资源(分离)

4、intpthread_detach(pthread_tthread);

原因:线程的一个特点是:除非线程是被分离了的,否则在线程推出时,它的资源是不会被释放的。

答:当使用此函数后,在线程退出后其所占用的资源就可以被回收了。

●参数:需要分离的线程的ID;

●返回值:成功返回0,失败返回一个错误码:EINVAL(此线程不可分离)、ESRCH(不存在此线程)

 

//等待一个线程(接合)

5、intpthread_join(pthread_tthread,void **value_ptr);

调用此函数的线程需要等待参数1线程完成后才能继续执行。

●参数1:即要等待的线程的ID;

●参数2:为一个指针提供一个位置,这个返回值是参数1线程传递给其pthread_exit或return。如果设置其为NULL,调用程序就不会对目标线程的返回状态进行检索了。

●返回值:成功返回0,错误返回EINVAL(此线程不可接合)、ESRCH(不存在此线程)

//注:如果线程没有被分离,并且执行pthread_join(pthread_self()),那么该线程将被一直挂起,造成死锁。

有些POSIX可检测到死锁,并强制pthread_join函数带着错误EDEADLK返回,但并不一定要进行这种检测。

 

//退出线程,而不退出进程

6、intpthread_exit(void *value_ptr);

调用此函数的线程将中止运行,此线程会传递一个指针到这个函数参数中,可以用来指向线程的返回值。回顾前面pthread_join函数的参数void**value_ptr正是保存了pthread_exit函数的参数void *value_ptr的地址。这里要注意,pthread_exit的参数valude_ptr必须指向线程退出后仍然存在的数据。

 

//终止另一个线程

7、intpthread_cancel(pthread_tthread)

●参数:即需要取消的线程的ID

●返回值:成功返回0,失败返回一个非零错误码


接下来贴一个最简单的多线程例子:

threadTest.c

/*
 *最简单的多线程函数:
 *main函数相当于主线程,执行打印
 *接着又创建了两个子线程,执行打印
 */
#include<stdio.h>
#include<pthread.h>

void *threadfunc(void *pVoid)
{
		int thread_id = (int)pVoid;
		printf("Child thread%d says:hello world!\n",thread_id);
		return NULL;
}

void main()
{
	pthread_t thread1;
	pthread_t thread2;
	pthread_create(&thread1,NULL,threadfunc,1);		
	pthread_create(&thread2,NULL,threadfunc,2);
	printf("main thread says:hello world!\n");
	return 0;
}


编译:

gcc -o threadTest threadTest.c -lpthread 

//pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数:

执行:



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值