您的位置:首页 > 运维架构 > Linux

linux线程

2016-03-02 20:15 441 查看
1.线程概述

        线程定义为进程内一个执行单元或可调度实体。进程是资源拥有单位,而线程是调度单位。多线程的进程在同一地址空间内包括多个不同的控制流,也即属于同一进程下的线程,它们共享进程拥有的资源,如代码、数据、文件等。线程也独占一些资源,如堆栈、程序技术器等。多线程系统的优点包括对用户相应的改进、进程内的资源共享,以及利用多处理器体系结构的便利。从实现的角度看,把线程分为用户级线程和内核级线程。用户级线程是程序员可见的,用户空间的线程库通常用以管理用户级线程,线程库提供对线程创建、调度和管理的支持。内核级线程由操作系统支持和管理,在内核空间实现线程床I安、调度和管理。用户级线程和内核级线程相比,优点是创建和管理要更快;缺点是得到CPU的时间更少,当一个线程阻塞时,连累其他线程全部阻塞。有三种不同模型将用户级线程和内核级线程关联起来:多对一模型将多个用户线程映射到一个内核线程;一对一模型将每个用户线程映射到一个相应的内核线程;多对多模型将多个用户线程在同样数量的内核线程之间切换。Linux把线程和进程一视同仁,每个线程拥有属于自己的task_struct结构,不过线程本身拥有的资源少,共享进程的资源,如:共享地址空间、文件系统资源、文件描述符合信号处理程序。在Linux环境下的实际应用中,一般使用Pthread提供的API来编写多线程应用程序,使用头文件pthread.h,编译时要加上-lpthread选项。

2.线程的创建

        函数pthread_create()用于创建线程。原线程调用pthread_create()后立即返回,继续执行之后的指令,同时,新线程开始执行线程函数。Linux调度各个线程,它们并发执行,因此程序不能依赖两个线程得到执行的特定先后顺序。

                pthread_create函数说明如下:

                        所需文件头:#include<pthread.h>

                        函数功能:创建一个 线程

                        函数原型:int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void * (*start_routine)(void *),void * arg);

                        函数参数:第1个参数:产生线程的标识符;第2个参数:所产生线程的属性,通常设为NULL;第3个参数:新的线程所执行的函数代码;第4个参数:新的线程函数的参数

                        函数返回值:执行成功返回0,失败返回-1,而错误号保存在全局变量errno中。

        pthread_create成功返回后,新创建的线程的id被填入到thread参数所指向的内存单元。线程id的类型是thread_t,它只在当前进程中保证是唯一的,Linux中的thread_t是一个地址值,调用pthread_self得到线程id。

        下面的程序使用pthread线程库创建一个 新线程,在父进程和新线程中分别显示进程id和线程id数值。

                                                                                  



                                                                                  



        线程有两种退出方式:一种是当线程执行的函数返回时,线程也结束,线程函数的返回值也被称为线程的返回值。另一种方式是是线程显示的调用pthread_exit函数。线程创建完毕后,创建线程的进程可以调用pthread_join()函数等待被创建的线程的结束。

        下面的函数演示线程的退出和等待:

                                                                                  



                                                                                  



3.线程同步与互斥

        当并发执行的线程共享数据时,各个线程会改写共享的数据,由于CPU调整顺序的不确定性,造成线程运行结果的不确定性。所以必须为访问同一资源的各个线程提供互斥机制。临界区可以实现同一时刻只能有一个线程访问共享数据。线程在并发执行时为了保证结果的可再现性,各个线程执行序列必须加以限制以保证互斥地使用临街资源,相互合作完成任务。多个相关的线程在执行次序上的协调称为线程同步。用于保证多个线程在执行次序上的协调关系的相应机制称为线程同步机制。Pthread线程库提供了多种方式来处理线程同步互斥机制,最常用的是互斥所、条件变量和信号量。

                 互斥锁:通过锁机制实现线程间的互斥,同一时刻只有一个线程可以锁定它。当一个锁被某个线程锁定的时候,如果有另外一个线程尝试锁定这个临界区,则第二个线程会被阻塞,或者被置于等待状态。只有当第一个线程释放了对临界区的锁定,第二个线程才能从阻塞状态恢复运行。

                 条件变量:利用线程间共享的全局变量进行同步的一种机制。

                 信号量:用信号量可以实现创痛操作系统P、V操作。

        下面的程序显示了访问共享变量冲突的情况,创建的两个线程,counter的初值是0,两个线程各自把counter的值加10,结果应该是20,但实际运行结果却不是。

                                                                                 



                                                                                 



        互斥锁机制中,获得锁的线程可以完成“读/修改/写”的操作,然后释放锁给其他线程,没有获得锁的线程只能等待而不能访问共享数据,这样“读/修改/写”操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其他处理器上并行做这个操作。下面介绍下互斥锁常用的函数:

                pthread_mutex_init()用来初始化mutex,并且使用mutexattr参数来传递初始化mutex需要定义的属性,如果属性指针为NULL,则默认属性将被赋予新建的临界区。

                pthread_mutex_lock()用来获得mutex,如果当前进程想要获取的mutex已经被其他线程锁定,那么调用pthread_mutex_lock()将使当前进程处于等待状态。

                pthread_mutex_trylock()函数和pthread_mutex_lock函数功能基本一致,只不过此函数时“尝试”获得mutex,如果不能获得,其将立刻返回,并继续线程的执行。

                pthread_mutex_unlock()函数用来释放mutex锁,以便供其他线程使用。

                pthread_mutex_destroy()函数用于在使用完毕mutex后,释放mutexs资源。

        下面是一个应用互斥锁的例子

                                                                                 



                                                                                 



                                                                                 



        互斥锁只有两种状态:锁定和非锁定。而条件变量通过允许进程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其他的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般来说,条件变量可以用来进行线程间的同步。函数pthread_cond_init()用来初始化一个条件变量,函数pthread_cond_destroy()用来释放一个条件变量。函数pthread_cond_wait()用来使线程阻塞在一个条件变量上。函数pthread_cond_signal()和pthread_cond_broadcast()用来唤醒线程。

        下面的程序演示了一个生产者-消费者的例子,生产者生产一个结构体串在链表头,消费者从表头取走结构体。

                                                                                 



                                                                                 



                                                                                 



        信号量是操作系统中解决进程或线程同步与互斥地最重要的机制之一。与信号量有关的函数,它们都在头文件/usr/include/semaphore.h中定义。信号量的数据类型结构为sem_t,它本质上是一个长整型的数。

                a、初始化一个信号量:sem_init()。它的原型是:int sem_init(sem_t * sem,int pshared, unsigned int value);sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的线程共享;value给出了信号量的初始值。

                b、阻塞线程:sem_wait(sem_t * sem)被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减1,表明公共资源经使用后减少。函数sem_trywait(sem_t  *  sem)是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减1,相当于P操作。

                c、增加信号量的值:sem_post(sem_t * sem)。当线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不再阻塞,选择机制同样是由线程的调度策略决定的,相当于V操作。

                d、释放信号量资源:sem_destroy(sem_t  *  sem),用来释放系统中信号量sem所占有的资源。

        下面的程序使用linux的Pthread线程库,创建2个生产者线程和2个消费者线程。生产者线程计算当前的时间,把时间和线程ID作为一个消息,把消息放入缓冲区,消费者线程从缓冲区读出一个消息并显示消息。缓冲区大小为3个,每个生产者线程生产3个消息,每个消费者线程消费3个消息,即生产和消费分别为20次。

                                                                                



                                                                                



                                                                                



                                                                                



                                                                                



 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息