b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

多线程编程_线程 线程池_windows线程编程

电脑杂谈  发布时间:2019-06-18 09:11:08  来源:网络整理

windows线程编程_多线程编程_线程 线程池

一、线程模型:

线程是程序中完成一个独立任务的完整执行序列,即一个可调度的实体。根据运行环境和调度者的身份,线程可分为内核线程和用户线程。

内核线程:运行在内核空间,由内核来调度;

用户线程:运行在用户空间,由线程库来调用。

当进程的一个内核线程获得CPU的使用权时,它就加载并运行一个用户线程。可见,内核程序相当于用户线程运行的容器。一个进程可以拥有M个内核线程和N个用户线程,其中M≤N。并且在一个系统的所有进程中,M和N的比值都是固定的。按照M:N的取值,线程的实现方式可分为三种模式:完全在用户空间实现、完全由内核调度和双层调度。

用过unix操作系统的读者知道进程,在unix操作系统中,每个应用程序的执行都在操作系统内核中登记一个进程标志,操作系统根据分配的标志对应用程序的执行进行调度和系统资源分配,但进程和线程有什么区别呢。linux支持内核级的线程,但它将线程定义为进程的另一个“执行上下文”,从而简化了进程/线程之间的关系和调度程序的设计,它的线程库提供了和posix兼容的线程同步机制。如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止,由于从main函数return也相当于调用exit,为了防止新创建的线程还没有得到执行就终止,我们在main函数return之前延时1秒,这只是一种权宜之计,即使主线程等待1秒,内核也不一定会调度新创建的线程执行,1.2我们会看到更好的办法。

完全在用户空间实现的线程的优点是:创建和调度线程都无需内核的干预,因此速度相当快。并且由于它不占用额外的内核资源,所以即使一个进程创建了很多线程,也不会对系统性能造成明显的影响。其缺点是:对于多处理器系统,一个进程的多线程无法运行在不同的CPU上,因为内核是按照其最小调度单位来分配CPU的。此外,线程的优先级只对同一个进程中的线程有效,比较不同进程中的线程的优先级没有意义。

将多个用户级线程映射到一个内核级线程,线程管理在用户空间完成。1) 多对一模型将多个用户级线程映射到一个内核级线程,线程管理在用户空间完成。lwp还是和前面提到的一样,具有内核线程支持,是内核的调度单元,并且用户线程的系统调用要通过lwp,因此进程中某个用户线程的阻塞不会影响整个进程的执行。

3、双层调度模式是前两种实现模式的混合体:内核调度M个内核线程,线程库调用N个用户线程。这种线程实现方式结合了前两种方式的优点:不但不会消耗过多的内核资源,而且线程切换速度也较快,同时它可以充分利用多处理器的优势。

二、Linux线程库

Linux上最有名的线程库是LinuxTreads和NPTL,它们都是采用1:1的方式实现的。

用户可以使用如下命令来查看当前系统上所使用的线程库:

$ getconf GNU_LIBPTHREAD_VERSION

windows线程编程_线程 线程池_多线程编程

NPTL 2.14.90

三、创建线程和结束线程

1、pthread_create:创建一个线程

#include<pthread.h>

intpthread_create( pthread_t*thread,constpthread_att_t*attr,

void*(*start_routine)(void*),void*arg);

thread参数是新线程的标识符,后续pthread_t*函数通过它来引用新的线程。其类型pthread_t的定义如下:

#include<bits/pthreadtypes.h>

attr参数用于设置新线程的属性。给它传递NULL表示使用默认的线程属性。start_routine和arg参数分别指定新线程将运行的函数及其参数。

两个方法都可以向线程池提交任务,execute ()方法的返回类型是 void,它定义在 executor 接口中, 而 submit ()方法可以返回持有计算结果的 future 对象,它定义在 executorservice 接口中,它扩展了 executor 接口,其它线程池类像 threadpoolexecutor 和 scheduledthreadpoolexecutor 都有这些方法。两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在executor接口中,而submit()方法可以返回持有计算结果的future对象,它定义在executorservice接口中,它扩展了executor接口,其它线程池类像threadpoolexecutor和scheduledthreadpoolexecutor都有这些方法。设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方。

2、pthread_exit

在程序中p是一个派生类对象,我们将其强制指向一个基类对象,首先通过p指针调用m函数,因为基类中包含有m函数,这一句没有问题,之后通过p指针调用f函数。由于是用createremotethread函数远程执行getmodulehandle函数.所以无法直接从getmodulehandle函数得到返回值(也就是dll句柄).在此必在waitforsingleobject函数之后用getexitcodethread函数来获取线程的退出代码.如果线程正确返回.该退出代码就是线程函数(getmodulehandle)的返回值然后再用同样的方法用createremotethread远程创建线程调用freelibrary函数来卸载该dll。函数指针,即指向函数在内存映射中的首地址的指针,通过函数指针,可以将函数作为参数传递给另一个函数,并在适当的时候调用,从而实现异步通信等功能。

#include<pthread.h>

线程 线程池_windows线程编程_多线程编程

pthread_exit函数通过retval参数向线程的回收者传递其退出信息。它执行完之后不会返回到调用者,而且永远不会失败。

3、pthread_join

先说说用getmodulehandle函数获取.此方法需要用createremotethread函数在远程进程中创建线程,让该线程调用kernel32.dll中的getmodulehandle函数.但是只这样还不行.因为getmodulehandle函数需要以dll模块文件名做参数.既然是远程线程调用getmodulehandle函数.还需要先把dll模块文件名写入目标进程的地址空间中.最后用createremotethread函数创建线程执行getmodulehandle函数来获取dll句柄。于进程中的zombie process,即还有一部分资源没有被回收(退出状态码),所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码多线程编程,回收其资源(类似。第22行调用pthread_join()函数等待线程的结束,通过函数的第一个参数指定要等待的线程,第二个参数用来接收线程函数返回值。

#include<pthread.h>

intpthread_join(pthread_tthread,void**retval);

c、当其他线程调用pthread_cond_signal或pthread_cond_broadcast时多线程编程,会唤醒相应条件变量等待的线程,此时被唤醒的线程,可以参与调度了,此时被唤醒的线程继续执行pthread_cond_wait()函数,函数pthread_cond_wait()返回之前,会重新给条件变量对应的互斥量上锁,在这里就是nready.mutex,若该函数成功返回,则当前线程有重新获得了nready.mutex锁,当然nready.mutex也可能被其他线程继续占有,此时线程再次阻塞。由于是用createremotethread函数远程执行getmodulehandle函数.所以无法直接从getmodulehandle函数得到返回值(也就是dll句柄).在此必在waitforsingleobject函数之后用getexitcodethread函数来获取线程的退出代码.如果线程正确返回.该退出代码就是线程函数(getmodulehandle)的返回值然后再用同样的方法用createremotethread远程创建线程调用freelibrary函数来卸载该dll。webjx网页教学提示: 对于worker线程,终止线程可以使用线程的退出码作为返回值从线程函数返回。

错误码

描述

EDEADLK

可能引起死锁。比如两个线程互相针对对方调用pthread_join,或者线程对自身调用pthread_join。

EINVAL

目标线程是不可回收的,或者已有其他线程在回收该目标线程

ESRCH

线程 线程池_windows线程编程_多线程编程

目标线程不存在

4、pthread_cancel

有时候我们希望异常终止一个线程,即取消线程。

#include<pthread.h>

intpthread_cancel(pthread_tthread);

对于worker线程,终止线程可以使用线程的退出码作为返回值从线程函数返回。由于是用createremotethread函数远程执行getmodulehandle函数.所以无法直接从getmodulehandle函数得到返回值(也就是dll句柄).在此必在waitforsingleobject函数之后用getexitcodethread函数来获取线程的退出代码.如果线程正确返回.该退出代码就是线程函数(getmodulehandle)的返回值然后再用同样的方法用createremotethread远程创建线程调用freelibrary函数来卸载该dll。如果目标线程是其他线程,send消息后直接返回,之后本线程应该用getmessage来响应其他线程post回来的send处理完毕的通知,该通知的处理函数会调用注册的回调。

#include<pthread.h>

int pthread_setcancelstate(int state,int* oldstate);

int pthread_setcanceltype(int type,int* oldtype);

这两个函数的第一个参数分别用于设置线程的取消状态(是否取消)和取消类型(如何取消),第二个参数分别记录线程原来的取消状态和取消类型。

四、POSIX信号量

专门用于线程同步的机制:POSIX信号量、互斥量和条件变量。

常用的POSIX信号量函数是下面5个:

多线程编程_windows线程编程_线程 线程池

#include <semaphore.h>

int sem_init(sem_t* sem,int pshared,unsigned int value);

int sem_destroy(sem_t* sem);

int sem_wait(sem_t* sem);

int sem_trywait(sem_t* sem);

int sem_post(sem_t* sem);

这些函数的第一个参数sem指向被操作的信号量。

sem_init函数用于初始化一个未命名的信号量。pshared参数指定信号量的类型。如果其值为0,就表示这个信号量是当前进程的局部信号量,否则该信号量就可以在多个进程之间共享。value参数指定信号量的初始值。此外,初始化一个已经被初始化的信号量将导致不可预期的结果。

当等待成功后 waitforsingleobject函数会将互斥量置为无信号状态,这样其他的线程就不能获得使用权而需要继续等待。两个子线程在运行过程中,通过调用waitforsingleobject等待该互斥量,如果此时互斥量处于无信号态,则在等待线程被系统挂起,一直等到互斥量变为信号态才继续运行,waitforsingleobject返回的同时,将使互斥量再次变为无信号态。比如有一个占用互斥量的线程在调用releasemutex()触发互斥量前就意外终止了(相当于该互斥量被“遗弃”了),那么所有等待这个互斥量的线程是否会由于该互斥量无法被触发而陷入一个无穷的等待过程中了。

使用该函数事件和信号量的触发,对于信号量的触发可以使信号量的值减1.该函数每次只能多个对象。如果线程试图减少一个值为零的信号量的值,它就会阻塞,直到另一个线程增加该信号量的值。函数sem_wait(sem_t*sem)被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。

sem_trywait与sem_wait函数相似,不过它始终立即返回,而不论被操作的信号量是否具有非0值,相当于sem_wait的非阻塞版本。当信号量的值非0时,sem_trywait对信号量执行减1操作。当信号量的值为0时,它将返回-1并设置errno为EAGAIN。

sem_post函数以原子操作的方式将信号量的值加1。当信号量的值大于0时,其他正在调用sem_wait等待信号的线程将被唤醒。

上面这些函数,成功时返回0,失败则返回-1并设置errno。

五、互斥锁


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-106818-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...