概述
1.线程的定义
1.1 线程:线程是进程中的一个独立的代码块。说白了,其实它就是个函数,只不过再也不用像以前的函数调用来调用它。而是通过pthread_create函数来创建它,也就是告诉内核,这个函数是个线程,今后交给你来调度了。
如果从以上的定义来看,那么很明显,线程是拥有自己的栈空间(局部变量),但是共享全局变量、文件描述符等。
注意:这边区别一下和fork系统调用创建出来的进程进行一下比较:fork创建出的是一个新的进程,因此他拥有自己的变量和PID,而不需要和父进程进行共享。
1.2 在单核cpu上,其实多线程并不能实现并行运行,也是通过轮询的方式来实现的。
思考:既然在单核cpu中,多线程做不到并行运行,那么这样创建线程有意义吗?
无意义:如果不启用多线程,那么一个进程运行下来时间差不多等于进程在调度过程中所获得的时间,如果开启多线程,那么这部分时间还要加上线程切换的时间。
有意义:因为现在大多数的计算机或者项目中主要使用的是IO密集型,所以涉及IO就必然会等待。在进程调度的情况下,如果进程进入等待,就会立即调度,运行另一个进程。但是在线程等待中,如果一个线程等待了,那么会调度到另一个线程中运行,从这个方面看,多线程在单核cpu还是有意义的。(cpu密集型除外)
扩展:1.了解多线程在单核多cpu和多核单cpu中的运行。
2.了解用户态多线程和内核态多线程。
3.了解NPTL线程库
1.3 线程的优点和缺点:
优点:
- 相比于进程,开辟一个线程所消耗的资源远小于进程。
- 线程之间的通信机制相比于进程间的通信机制更加方便。
缺点:
- 线程调度时,内核需要保存线程状态,频繁调度,会浪费时间。
- 线程同步有加锁操作,会导致效率低。
- 一个线程奔溃会导致整个进程都奔溃。
1.4 线程的函数:
1.4.1 pthread_create
#include <pthread.h> int pthread_create(pthread_t *thread, pthread_attr_t *attr, void*(*start_routine)(void*), void *arg);
作用:创建一个新的线程。
- 第一个参数:只想pthread_t类型数据的指针,线程被创建时,这个指针所指向的变量将会被写入一个标识符,用于后面对线程的引用。
- 第二个参数:用于设置线程的属性,如果不需要设置线程的属性,直接设置参数为NULL。
- 第三个参数:执行的代码函数。
- 第四个参数:传递给该函数的参数。
- 返回值:成功返回0,失败返回错误代码
线程创建时,并不能保证哪个线程会先运行。
#include <pthread.h> pthread_t ntid; void printfs(const char *s) { pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); //获取当前线程的ID printf("%s pid %lu tid %lu (0x%1x) n", s,(unsigned long) pid, (unsigned long)tid, (unsigned long)tid); } void *thr_fn(void *arg) { printfs("new thread:"); return((void *) 0); } int main(void) { int err; err = pthread_create(&ntid, NULL, thr_fn, NULL); if(err != 0) err_exit(err, "can't create thread"); printfs("main thread:"); sleep(1); exit(0); }
以上例程有两个需要注意的点:
- 需要特别注意主线程和新线程的竞争(线程创建时,并不能保证哪个线程会先运行),所以目前这里采用的主线程休眠一秒,如果主线程不休眠,并且是主线程先运行,那么久会直接exit,新线程就没有运行的机会。
- 新线程是通过pthread_self来获取自己的线程ID。而不是通过ntid。因为主线程通过pthread_create创建新线程,会把新线程的ID存在放第一个参数也就是ntid上。但是新建的线程并不能安全的使用它。理由是:如果新线程在主线程调用pthread_create返回之前就运行了。那么新线程看到的是未初始化的ntid内容,这个内容并不是正确的线程ID。
导致以上两个特别需要注意的原因都是线程创建时,并不能保证哪个线程会先运行。
1.4.2 pthread_exit
如果进程中的任意线程调用了exit、_Exit或者_exit,那么整个进程就会终止(注意,不要在某个线程调用这几个退出函数,因此一旦某个线程调用,那么进程就会退出,而一个进程中通过有多个线程,这样会导致其他线程也一起退出)。
单个线程在不终止整个进程的情况下可以通过三种方式退出:
- 线程可以简单地从启动例程中返回(函数运行结束退出),返回值是线程的退出码(return)。
- 线程可以被同一进程中的其他线程取消。
- 线程调用pthread_exit。
#include <pthread.h> void pthread_exit(void *retval)
作用:终止调用它的线程并返回一个指向某个对象的指针(即void *retval)。
注意:绝不能用它返回一个指向局部变量的指针,因为线程调用该函数后,局部变量就将不存在,就会引起严重的程序问题。
1.4.3 pthread_join
#include <pthread.h> int thread_join(pthread_t th, void **thread_return);
作用:等价于进程中用来收集子进程信息的wait函数。
第一个参数:指定将要等待的线程。
第二个参数:这个参数是一个指针,它指向另一个指针,后者指向线程的返回值。这边主要用于同步,即上面的pthread_exit结束完当前的线程会返回他参数里面的数据给pthread_join,然后在主线程中可以调用这个数据。
当一个线程通过调用pthread_exit退出或者简单从启动例程中返回时,进程中的其他线程可以通过使用pthread_join函数获得该线程的退出状态。
pthread_create和pthread_exit函数的无类型指针参数可以传递的值不止一个,这个指针可以传递包含复杂信息的结构的地址。但是注意,这个结构所使用的内存在调用者完成调用以后必须仍然有效。例如:在调用线程的栈上分配了该结构,那么其他的线程在使用这个结果的时候内存内容可能已经改变了。又比如:线程在自己的栈上分配了一个结构,然后把指向这个结构的指针传给pthread_exit,那么调用pthread_join的线程试图使用该结构时,这个栈有可能已经被撤销。
由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。pthread_join可以用于将当前线程挂起,等待线程的结束。这个函数是一个线程阻塞的函数,调用它的函数将一直被等待的线程结束为止,当函数返回时,被等待线程的资源就被收回。
1.4.4 pthread_cancel
#include <pthread.h> int pthread_cancel(pthread_t tid); 返回值: 成功:0; 失败:返回错误编号
线程可以通过调用pthread_cancel函数来请求取消同一进程中的其他线程。
在默认情况下,pthread_cancel函数会使得由tid标识的线程的行为表现为如同调用了参数为PTHREAD_CANCELED的pthread_exit函数。但是,线程可以选择忽略取消或者控制如何被取消。需要特别注意的是:pthread_cancel并不等待线程终止,它仅仅提出请求。
2.线程的属性
本人目前使用的内核版本是4.2的。但是找不到pthread_attr_t这个结构体属性。根据说明,这边特意地不显示pthread_attr_t的结构体属性。目前不太清楚为什么。
POSIX线程库中定义的线程属性对象pthread_attr_t,主要包含以下属性:
- 作用域(scope)
- 栈尺寸(stack size)
- 栈地址(stack address)
- 优先级(priority)
- 分离的状态(detached state)
- 调度策略和参数(scheduling policy and parameters)
初始化/销毁线程属性
#include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr);
detached state:这个属性允许我们是否对线程进行合并。默认属性是PTHREAD_CREATE_JOINABLE,所以默认是可以合并的。这个时候我们在主线程必须调用pthread_join对这个线程进行等待直到其结束,然后才释放自己占用的资源。当选择属性为PTHREAD_CREATE_DETACHED,这个时候线程属于分离属性状态,当这个线程结束时,会立马释放自身所占用的资源,无需外部来对它进行释放。
设置/获取线程分离属性
#include <pthread.h> int pthread_attr_setdetachstate(pthread_attr_t *attr, int detached); int pthread_attr_getdetachstate(const ptherad_attr_t *attr, int *detachedstate);
目前线程属性暂时用到这个,后面再详细学习。
转载于:https://www.cnblogs.com/zhuangquan/p/10895839.html
最后
以上就是彩色彩虹为你收集整理的《线程的概念》的全部内容,希望文章能够帮你解决《线程的概念》所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复