标签 python 下的文章

这两天读了一下Python的Condition实现源码,是实现Queue的工具之一,发现是非常朴素的sleep->loop->query模式。源码很少,直接贴出,就不做注释了:)

def wait(self, timeout=None):
        """Wait until notified or until a timeout occurs.
 
        If the calling thread has not acquired the lock when this method is
        called, a RuntimeError is raised.
 
        This method releases the underlying lock, and then blocks until it is
        awakened by a notify() or notifyAll() call for the same condition
        variable in another thread, or until the optional timeout occurs. Once
        awakened or timed out, it re-acquires the lock and returns.
 
        When the timeout argument is present and not None, it should be a
        floating point number specifying a timeout for the operation in seconds
        (or fractions thereof).
 
        When the underlying lock is an RLock, it is not released using its
        release() method, since this may not actually unlock the lock when it
        was acquired multiple times recursively. Instead, an internal interface
        of the RLock class is used, which really unlocks it even when it has
        been recursively acquired several times. Another internal interface is
        then used to restore the recursion level when the lock is reacquired.
 
        """
        if not self._is_owned():
            raise RuntimeError("cannot wait on un-acquired lock")
        waiter = _allocate_lock()
        waiter.acquire()
        self.__waiters.append(waiter)
        saved_state = self._release_save()
        try:    # restore state no matter what (e.g., KeyboardInterrupt)
            if timeout is None:
                waiter.acquire()
                if __debug__:
                    self._note("%s.wait(): got it", self)
            else:
                # Balancing act:  We can't afford a pure busy loop, so we
                # have to sleep; but if we sleep the whole timeout time,
                # we'll be unresponsive.  The scheme here sleeps very
                # little at first, longer as time goes on, but never longer
                # than 20 times per second (or the timeout time remaining).
                endtime = _time() + timeout
                delay = 0.0005 # 500 us -> initial delay of 1 ms
                while True:
                    gotit = waiter.acquire(0)
                    if gotit:
                        break
                    remaining = endtime - _time()
                    if remaining <= 0:
                        break
                    delay = min(delay * 2, remaining, .05)
                    _sleep(delay)
                if not gotit:
                    if __debug__:
                        self._note("%s.wait(%s): timed out", self, timeout)
                    try:
                        self.__waiters.remove(waiter)
                    except ValueError:
                        pass
                else:
                    if __debug__:
                        self._note("%s.wait(%s): got it", self, timeout)
        finally:
            self._acquire_restore(saved_state)

引子

以前对Gevent(Greenlet)为什么比Thread快,只有一个隐约的理解,之前的看到过的说法是,上下文切换的成本上,Greenlet比Thread低很多,但是具体低在哪些地方呢?

刚好这段时间阅读了CSAPP,对这个问题又有了新的看待的角度:)

参考了如下三篇文章和greenlet的实现:

https://www.ibm.com/developerworks/cn/linux/kernel/l-thread/

http://stackoverflow.com/questions/15556718/greenlet-vs-threads

http://www.jianshu.com/p/cd41c14b19f4

Greenlet VS Thread

相同点

都进行了寄存器内容的保存,保存了栈的内容以便将来恢复
不同点

如下几件事情,是协程切换不用做的事情

少了一些system call,自己主动保存/恢复寄存器信息。
不需要管理复杂的线程数据结构然后再到Python VM里面管理字节码,比如CLONE_FS,CLONE_FILES,CLONE_SIGHAND等操作都不需要进行
没有mmap操作
综上所述,协程的创建和上下文切换成本中,两者都需要进行寄存器状态保存,但用户态的协程,少了很多额外操作,因此更加轻量。

Why?

Web产品一旦上线,重启应用就会造成业务中断,对于实时性要求很高或者业务关联紧密的应用,重启程序是非常重的代价。

将代码对象序列化之后保存到存储内(比如redis, 关系数据库),在运行业务的时候通过制定的路由机制加载这部分业务。

对于线上应用,使用这种方式可以更加方便的部署新业务。

对于性能要求不是很高的场合,可以牺牲一些性能做代码动态装载。

- 阅读剩余部分 -