多线程总结上

来源:互联网 发布:数组大小可以任意改变 编辑:程序博客网 时间:2024/05/01 12:56
虚拟计时器 : 进程状态是R 用户态的运行时间
实用计时器  :进程状态是R 用户态和内核态的总运       
                      行时间
——————————————————
多线程   协程
上下文:进程执行的线程
上下文切换:进程打开的标准输入、标准输出和错误输出,当切换到另一个进程时,需要进行压栈(即压入内存),把一个进程的现场保存下来,然后去执行另外一个进程的现场
    线程通常叫做轻量级进程。即线程都在一个进程中,当写一个二进制执行起来时,其本身就是一个线程,这里这个进程中只有一个线程。假如创建了多个线程,则他们共享内存地址空间,如在一个线程中malloc堆内存,则在另一个线程中就可以直接拿这个p去访问堆内存 ,是一个更加接近于执行体的概念(它只能去读每一条代码,其它东西都是跟别人共享的,不过它有局部变量,类似于在C语言中局部变量只在其最近的大括号中有效,所以线程唯一拥有的就是其栈空间(自动分配的)),拥有独立的执行序列(即各自走各自的代码),是进程的基本调度单元,每个进程至少都有一个main线程。它与同进程中的其他线程共享进程空间{堆 代码(代码段) 数据 文件描述符 信号等},大大减少了上下文切换的开销

700ms  进程启动时间
1ms    线程启动时间

线程和进程在使用上各有优缺点:线程执行开销小,占用的CPU少,线程之间的切换快,但不利于资源的管理和保护(如线程在操作堆内存,如踩内存,结果别人去访问自己的内存时被踩了,这时所有的线程都崩溃。即任何一个线程出错,其它任何线程都挂掉。因为踩到异常之后,系统会给进程发一个信号,这时每个线程都会接收到SIGSEGV信号);而进程正相反。从可移植性来讲,多进程的可移植性要好些(因为出问题更容易分析)
要注意的是,由于线程共享了进程的资源和地址空间,因此,任何线程对系统资源的操作都会给其他线程带来影响。(所以不要发进程信号)除了栈空间,其它都是共享的


线程调试难度更大,凡涉及到内存的东西,gdb调试不好使,一般采用打印的方式来调试

4G进程地址空间最上面一段是内核映射过来的(不是内核实际地址),内核中的线程可以看到它的所有物理内存

线程创建的Linux实现
Linux的线程是通过用户级的函数库实现的,一般采用pthread线程库实现线程的访问和控制。它用第3方posix标准的pthread,具有良好的可移植性。  编译的时候要在后面加上 –lpthread
                   创建                 退出                等待
多进程         fork()                 exit()               wait()
多线程   pthread_create   pthread_exit()   pthread_join()

创建线程实际上就是确定调用该线程函数的入口点(通过pthread函数,把函数的名字传给pthread,这时线程启动时就知道自己从哪里开始,即从哪个函数开始,而该函数就是线程的入口点,如果多个线程都用相同函数入口点也无所谓,因为他们本身就是并行执行的(可以并发),创建后就放在一个就绪队列,这时通过分时间片往下执行),线程的创建采用函数pthread_create。在线程创建以后,就开始运行相关的线程函数,在该函数(即线程入口函数a)运行完之后,线程就退出(a{可以调用很多层函数,所有代码都在尾括号内就行}的结尾括号一结束,线程就结束了),这也是线程退出的一种方式。另一种线程退出的方式是使用函数pthread_exit()函数,这是线程主动退出行为。这里要注意的是,在使用线程函数时,不能随意使用exit退出函数进行出错处理,由于exit的作用是使调用进程终止,往往一个进程包括了多个线程,所以在线程中通常使用pthread_exit函数来代替进程中的退出函数exit。
pthread_join函数可以用于将当前线程挂起,等待线程的结束。这个函数是一个线程阻塞函数,调用它的函数将一直等待直到被等待的线程结束为止,当函数返回时,被等待线程的 资源被回收。

函数原型:

#include <pthread.h>


int pthread_create(pthread_t* thread, pthread_attr_t * attr, void *(*start_routine)(void *), void * arg);
void pthread_exit (void *retval);

通常的形式为:

pthread_t pthid;

pthread_create(&pthid,NULL,pthfunc,NULL);pthread_create(&pthid,NULL,pthfunc,(void*)3);

pthread_exit(NULL);pthread_exit((void*)3);//3作为返回值被pthread_join函数捕获,然后清理系统资源。

函数pthread_create用来创建线程。返回值:成功,则返回0;失败,则返回-1。各参数描述如下:

·参数thread是传出参数,保存新线程的标识
线程ID:先定义一个整型ID,然后把地址放过来:pthread_t  th;    &th;
进程中用fork

·参数attr是一个结构体指针,结构中的元素分别指定新线程的运行属性,attr可以用pthread_attr_init等函数设置各成员的值,但通常传入为NULL 即可;


·参数start_routine是一个函数指针指向新线程的入口点函数,线程入口点函数带有一个void *的参数由pthread_create的第4个参数传入
传入参数、返回值都为指针的函数指针(即线程函数,函数名本身为地址)。该位置直接写函数名,其函数定义:
void* thread_enter(void* p)
{
   系统内部来调用这个函数的, 执行完这个函数,线程就结束了
}

·参数arg用于传递给第3个参数指向的入口点函数的参数,可以为NULL,表示不传递

函数pthread_exit表示线程的退出。其参数可以被其它线程用pthread_join函数捕获。

在编译多线程程序时,需要在后面加上 -lpthread,之前的库是在libc.so,而线程pthread的库文件不在这里,需要自己引入
ps -elLf  :查看创建的线程

在多线程程序中,如果返回值正确,但程序不能达到预期,则检查是否加上pthread_exit();或者是否加上头文件
在进程中,主进程和子进程都是独立的,而主线程和子线程共享除了栈空间之外的所有资源,故在主线程中需要缓冲时间(while or sleep)来等待子线程(因为主线程一旦结束,进程就结束了,资源被回收。所以实际使用是在主线程中加while),但子线程之间则不需要缓冲时间

3.3.线程终止清理函数
0 0