前言:
  最近帮朋友review其模块服务代码, 使用的是python的twisted网络框架. 鉴于之前并没有使用过, 于是决定好好研究一番.
  twisted的reactor模型很好的处理了网络IO事件, 以及定时任务触发. 但包处理后的业务逻辑操作, 需要根据具体的场景来决定.
  本文将讲述twisted如何实现half-sync/half-async的模式, 其线程池和defer模式是如何设计和使用的.

场景构造:
  twisted服务接受业务请求, 后端需要访问mysql. 由于mysql的接口是同步的, 如果安装twisted默认的方式处理话, 其业务操作(mysql)会阻塞reactor的IO事件循环. 这大大降低了twisted的服务能力.
  为了解决该类问题, twisted支持线程池. 把业务逻辑和IO事件分离, IO操作依旧是异步的, 而业务逻辑则采用线程池来处理.

  

工作线程池:
  在具体讲述defer模式之前, 先谈谈reactor自带的线程池, 这也符合使用half-sync/half-async模式的直观理解.
  先来构造下一个基础样例代码:

#! /usr/bin/python
#-*- coding: UTF-8 -*- from twisted.internet import reactor
from twisted.internet import protocol
from twisted.protocols.basic import LineReceiver import time class DemoProtocol(LineReceiver): def lineReceived(self, line):
# 进行数据包的处理
reactor.callInThread(self.handle_request, line) def handle_request(self, line):
"""
hanlde_request:
进行具体的业务逻辑处理
"""
# 边使用sleep(1)来代替模拟
time.sleep(1)
# 借助callFromThread响应结果
reactor.callFromThread(self.write_response, line) def write_response(self, result):
self.transport.write("ack:" + str(result) + "\r\n") class DemoProtocolFactory(protocol.Factory):
def buildProtocol(self, addr):
return DemoProtocol() reactor.listenTCP(9090, DemoProtocolFactory())
reactor.run()

  DemoProtocol在收到一行消息, 需要处理一个业务需耗时一秒, 于是其调用callInThread来借助reactor的线程池来执行.
  其callInThread的函数定义如下:

    def callInThread(self, _callable, *args, **kwargs):
self.getThreadPool().callInThread(_callable, *args, **kwargs)

  从中, 我们可以印证之前的观点, 借助线程池来完成耗时阻塞的业务工作.
  再来看一下callFromThread的函数定义:

    def callFromThread(self, f, *args, **kw):
assert callable(f), "%s is not callable" % (f,)
self.threadCallQueue.append((f, args, kw))
self.wakeUp()

  其作用是把回调放入主线程(也是reactor主事件循环)的待执行队列中, 并及时唤醒reactor.
  我们把写入响应的操作放入主循环中, 是为了让IO集中在主循环中进行, 避免潜在的线程不安全的问题.

defer模式:
  直接使用reactor的线程池, 非常容易实现half-sync/half-async的模式, 也让IO和业务逻辑隔离. 但reactor设计之初, 更倾向于隐藏其内部的线程池. 于是其引入了defer模式.
  让我们实现与上等同的代码片段:

#! /usr/bin/python
#-*- coding: UTF-8 -*- from twisted.internet import reactor
from twisted.internet import protocol
from twisted.protocols.basic import LineReceiver
from twisted.internet.threads import deferToThread import time class DemoProtocol(LineReceiver): def lineReceived(self, line):
# 进行数据包的处理
deferToThread(self.handle_request, line).addCallback(self.write_response) def handle_request(self, line):
"""
hanlde_request:
进行具体的业务逻辑处理
"""
# 边使用sleep(1)来代替模拟
time.sleep(1)
return line def write_response(self, result):
self.transport.write("ack:" + str(result) + "\r\n") class DemoProtocolFactory(protocol.Factory):
def buildProtocol(self, addr):
return DemoProtocol() reactor.listenTCP(9090, DemoProtocolFactory())
reactor.run()

  使用defer后, 代码更加的简洁. 其defer对象, 其实借用了线程池.
  threads.deferToThread定义如下:

def deferToThread(f, *args, **kwargs):
from twisted.internet import reactor
return deferToThreadPool(reactor, reactor.getThreadPool(),
f, *args, **kwargs) def deferToThreadPool(reactor, threadpool, f, *args, **kwargs):
d = defer.Deferred() def onResult(success, result):
if success:
reactor.callFromThread(d.callback, result)
else:
reactor.callFromThread(d.errback, result) threadpool.callInThreadWithCallback(onResult, f, *args, **kwargs) return d

  这边我们可以发现deferToThread, 就是间接调用了callInThread函数, 另一方面, 对其回调函数的执行结果, 进行了onCallback, 以及onErrback的调用. 这些回调函数在主线程中运行.
  defer模式简化了程序编写, 也改变了人们开发的思维模式.

测试回顾:
  使用telnet进行测试, 结果正常.
  
  另一方面, twisted的线程池, 其默认是采用延迟初始化的方式.
  服务开启时, 只有主线程一个, 随着请求的到来, 其按需产生更多的worker thread.
  而其线程池默认为10. 我们可以借助suggestThreadPoolSize方法来修改.

写在最后:
  
如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.

  

twisted的defer模式和线程池的更多相关文章

  1. 适配器、工厂模式、线程池、线程组、互斥锁、Timer类、Runtime类、单例设计模式(二十四)

    1.多线程方法 * Thread 里面的俩个方法* 1.yield让出CPU,又称为礼让线程* 2.setPriority()设置线程的优先级 * 优先级最大是10,Thread.MAX_PRIORI ...

  2. Java多线程设计模式(4)线程池模式

    前序: Thread-Per-Message Pattern,是一种对于每个命令或请求,都分配一个线程,由这个线程执行工作.它将“委托消息的一端”和“执行消息的一端”用两个不同的线程来实现.该线程模式 ...

  3. 【C#多线程】2.线程池简述+两种传统的异步模式

    线程池简述+两种传统的异步编程模式 1.线程池简述 首先我们要明确一点,编程中讲的线程与平时我们形容CPU几核几线程中的线程是不一样的,CPU线程是指逻辑处理器,比如4核8线程,讲的是这个cpu有8个 ...

  4. C# 多线程线程池( 一 )

    我们将在这里进一步讨论一些.NET类,以及他们在多线程编程中扮演的角色和怎么编程.它们是: System.Threading.ThreadPool 类 System.Threading.Timer 类 ...

  5. MYSQL线程池总结(一)

    线程池是Mysql5.6的一个核心功能,对于服务器应用而言,无论是web应用服务还是DB服务,高并发请求始终是一个绕不开的话题.当有大量请求并发访问时,一定伴随着资源的不断创建和释放,导致资源利用率低 ...

  6. android线程池ThreadPoolExecutor的理解

    android线程池ThreadPoolExecutor的理解 线程池 我自己理解看来.线程池顾名思义就是一个容器的意思,容纳的就是ThreadorRunable, 注意:每一个线程都是需要CPU分配 ...

  7. MySQL具体解释(7)-----------MySQL线程池总结(一)

    线程池是Mysql5.6的一个核心功能.对于server应用而言,不管是web应用服务还是DB服务,高并发请求始终是一个绕不开的话题.当有大量请求并发訪问时,一定伴随着资源的不断创建和释放.导致资源利 ...

  8. 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程

    额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...

  9. Linux高性能server规划——处理池和线程池

    进程池和线程池 池的概念 由于server的硬件资源"充裕".那么提高server性能的一个非常直接的方法就是以空间换时间.即"浪费"server的硬件资源.以 ...

随机推荐

  1. [css3]文字过多以省略号显示

    text-overflow:ellipsis; 优点: 1.不用通过程序限定字数 2.有利于SEO(实际上并未被截字,只是局限于宽度未被显示而已) width: 某个值; overflow: hidd ...

  2. ZOJ 3654 Letty's Math Class 模拟 难度:0

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4844 题意:给你一个只包含中括号和正整数,+,-,结果在longlong范围内 ...

  3. RM报表 刷新打印机列表

    procedure TRMReport.ShowPreparedReport; var s: string; lPreviewForm: TRMPreviewForm; begin RMCurRepo ...

  4. wp8.1 Study7: ListView 和GridView应用

    对于列表控件,WP8.1常用的是ListView.GridView.ListBox控件.其中前两个是从第三个继承来的. 1.ListView控件 它是展示垂直列表的,如下图所示.它十分适合展示数据. ...

  5. SPOJ COT2 树上找路径上不同值的个数

    题目大意 给出多个询问u , v , 求出u-v路径上点权值不同的个数 开始做的是COT1,用主席树写过了,理解起来不难 很高兴的跑去做第二道,完全跟普通数组区间求k个不同有很大区别,完全没思路 膜拜 ...

  6. C# Async/Await异步函数原理

    原理 与同步函数相比,CLR在执行异步函数时有几个不同的特点: 1.        并非一次完成,而且分多次完成 2.        并非由同一个线程完成,而是线程池每次动态分配一个线程来处理: 结合 ...

  7. hdu 1033 (bit masking, utilization of switch, '\0' as end of c string) 分类: hdoj 2015-06-15 21:47 37人阅读 评论(0) 收藏

    bit masking is very common on the lower level code. #include <cstdio> #include <algorithm&g ...

  8. 在shell脚本中使用函数

    转载请标明:http://www.cnblogs.com/winifred-tang94/ 对于在脚本中重复使用的功能模块,可以封装成为函数. shell脚本中函数的定义可以使用如下两种方式: a. ...

  9. mybatis写mapper文件注意事项(转)

    原文链接:http://wksandy.iteye.com/blog/1443133 xml中某些特殊符号作为内容信息时需要做转义,否则会对文件的合法性和使用造成影响 < < > & ...

  10. self进行weak化

    创建block匿名函数之前一般需要对self进行weak化,否则造成循环引用无法释放controller: __weak MyController *weakSelf = self 或者 __weak ...