当前位置:  技术问答>linux和unix

[百度分享]用户态线程库 (二)

    来源: 互联网  发布时间:2016-09-13

    本文导语:  异步  先来看看一个基本的异步程序是如何实现, 这里以aio为例   1 struct aiocb *aiocbp = new aiocb; 2 memset(aiocbp, 0, sizeof(aiocb)); 3 aiocbp->;aio_fildes = fd; 4 aiocbp->;aio_buf = write_buffer; 5 aiocbp->;aio_nbytes = write_buffer_si...

异步 
先来看看一个基本的异步程序是如何实现, 这里以aio为例 
 1 struct aiocb *aiocbp = new aiocb; 2 memset(aiocbp, 0, sizeof(aiocb)); 3 aiocbp->;aio_fildes = fd; 4 aiocbp->;aio_buf = write_buffer; 5 aiocbp->;aio_nbytes = write_buffer_size; 6 aiocbp->;aio_sigevent.sigev_notify = SIGEV_THREAD; 7 aiocbp->;aio_sigevent.notify_function = aio_callback; 8 aiocbp->;aio_sigevent.notify_attributes = NULL; 9 aiocbp->;aio_sigevent.sigev_value.sival_ptr = aiocbp10 11 aio_write(aiocbp);12 13 ....
这 里aio调用发起后,程序处理io, io结束之后会调用aio_callback进行返回 
aiocbp 本身以及一些变量都需要自己去记录和填写, 而且不能分配在栈上, 必须要注意相应的内存管理, 在callback中需要记的把aiocbp释放掉, 这样就需要注意在aio_write之后不能使用aiocbp 
这里只是一个buffer, 如果我们复杂一些,处理一个nshead请求, 那么这里的逻辑就变的很复杂, 需要在callback再度发起一次aio处理 
若程序的整个流程都按照异步的情况来处理那么可以看到,我们的业务逻辑是一个支离破碎的过程, 特别是对于一些小的流程处理,我们也必须要做这样的细致拆分 
其实这里还有一个问题就是默认系统中的aio是伪异步IO, 只是后台多线程模拟的异步IO处理, 这种处理对于磁盘IO可能问题还不大, 但是对于网络IO在调度和线程切换上不仅不会优于同步情况,而且还带来更大的性能开销。 
一般来说异步的基本模型如下 

有多个线程池,每个池处理不同的任务, 每个任务间采用事件驱动的方式来进行控制。当发现需要处理单独的任务的时候才激活,放到对应的线程池中去运行 
IO, CPU等操作都独立出来, 进行IO的时候,会主动切换到另外处理CPU的任务上, 等那边IO处理完毕会激活另外的事件回调进行处理。整个成为一个流水线式的作业,每个线程等待堵塞的地方都很少,一定程度上减少了上下文开销,可以进一步提高CPU的利用效率. 在ub 事件模型中采用的就是这种方式 
这样带来的一些好处 
对于每个任务而言, 一般线程数都很少,锁的影响很小 
处理IO不会堵塞,任务处理不和线程绑定,可以同时处理大量的请求 
但带来的最大问题就编程的复杂性。 
虽然一定程序度上这种异步的事件驱动模式更复合基本的状态机模型, 但是对于一些很简单的应用都要拆分成这么多不同阶段的任务,对于人的思维也是很不直观的。 
另外一个方面就是由于存在队列的情况, 处理任务会出现 快的很快, 慢的很慢, 这对于实时任务来说是不接受 
举个例子: 
任务1在 一个有4个线程对应4个CPU的线程池中运行, 来了5个任务, 那么第5个操作必须在前4个操作完成之后再进行. 这样在外部请求看来第5个任务花的时间是2倍的时间. 对于重吞吐的程序来说, 由于少了一些开销可以提高性能, 但是对于实时要求的程序来说,这个时候可能更希望前4个任务每个多花一点时间,使得第5个任务可以快一些完成,将单个请求花的时间平摊掉. 
不过上面的模型来处理我们实时要求高的业务,一般情况下也没有什么问题, 主要是我们一般请求处理的速度都是很快, CPU的时间片是百ms级,如果CPU没有占用超过100ms的时间, 其实是不会发生着这种主动切换的。我们切换多的地方主要还是各种IO,锁和系统调用。 
不同的任务分成多个线程池也是考虑避免某个特殊的任务把线程堵塞住。 
更轻量的线程 
严格说来, 异步化的事件驱动方式出现的比线程模式更早, 线程的出现本身就是为了减轻复杂的状态机编程。 在一般情况下确实给我们的程序带来了便利. 当时另一方面也带来了不少麻烦 
锁的应用, 需要小心死锁等问题. 另外互斥情况下的性能开销也是不可忽视的 
共享数据的修改和读取很容易出现问题, 而且不好排查,即使有了valgrind 这样工具,很多问题依然是很容易出现 
但另一方面, 这些问题对于上面的提到异步方式也是同样存在的, 本质上还是存在线程间的互斥问题。不过对于这种模式可以采用单进程的方式的来规避其中的一些问题, 但在利用多CPU的问题上还是需要另外的考虑 
前面提到过多线程的本质是记录环境的上下文,保存CPU的状态。 在glibc中提供了makecontext, swapcontext的方式来记录这些信息, 利用这两个调用,我们可以在用户态上实现轻量级的线程库。 
makecontext 创建一个新的上下文,可以传入我们自己定义的栈空间。它记录CPU的各种信息 
swapcontext 切换上下文,其实就是改变CPU的相关寄存器 状态,替换到另外的可执行的上下文中 

一个简单例子: 
 1 void call_thread(uint32_t p1, uint32_t p2) 2 { 3     ucontext_t* ctx = (ucontext_t*)((uint64_t)p2 | ((uint64_t)p1) 

    
 
 

您可能感兴趣的文章:

  • 输出java进程的jstack信息示例分享 通过线程堆栈信息分析java线程
  • Java多线程yield心得分享
  • JAVA实现多线程的两种方法实例分享
  • CSS属性参考手册 iis7站长之家
  • C#利用子线程刷新主线程分享教程
  • [百度分享]用户态线程库 (三)
  • android使用多线程更新ui示例分享
  • 线程安全的单例模式的几种实现方法分享
  • [百度分享]用户态线程库 (一)
  • .net重启iis线程池和iis站点程序代码分享
  • C#中跨线程访问控件问题解决方案分享
  • [百度分享]linux线程同步浅析 - 睡眠与唤醒的秘密
  • winform开发使用通用多线程基类分享(以队列形式)
  • c#多线程网络聊天程序代码分享(服务器端和客户端)
  • 杀掉oracle在线用户脚本分享
  • oracle创建删除用户示例分享(oracle删除用户命令及授权)
  • c#通过进程调用cmd判断登录用户权限代码分享
  • java用户名密码验证示例代码分享
  • Java中用户向系统传递参数的三种基本方式实例分享
  • 实现shell终端代码分享(可用户登录 实现系统命令)
  • mysql命令行下用户管理方法分享
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • ​docker之轻量虚拟化技术——docker实战分享
  • php利用腾讯ip分享计划获取地理位置示例分享
  • 点对点文件分享客户端 PeerProject
  • 网络文件分享 Giver
  • IM及文件分享软件 iptux
  • P2P分享软件 Alliance P2P
  • 文件分享软件 eMule Plus
  • P2P 文件分享软件 ShakesPeer
  • 分享页面内容插件 ContentShare
  • 社交分享按钮生成JS库 Socialite.js
  • P2P分享软件 Phex
  • 前端代码编辑和分享平台 RunJS
  • jQuery 分享按钮插件 Share Button
  • 昨天考过SCJP快乐大家分享
  • P2P文件分享 GNUnet
  • 响应式社交分享按钮 RRSSB
  • 谁有免费的英文Office2003(日文也成)分享一下吧!
  • 谁有qmail的日常维护,日志分析的资料分享下?
  • 代码分享
  • 谁有xml和libxml2的说明文档,分享一下吧
  • Java代码分享工具 Java Gems


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3