Python多線程編程

1、線程和進程

計算機的核心是CPU搬设,它承擔了所有的計算任務穴店。它就像一座工廠,時刻在運行拿穴。

假定工廠的電力有限泣洞,一次只能供給一個車間使用。也就是說默色,一個車間開工的時候球凰,其他車間都必須停工。背后的含義就是腿宰,單個CPU一次只能運行一個任務呕诉。

進程就好比工廠的車間,它代表CPU所能處理的單個任務吃度。任一時刻甩挫,CPU總是運行一個進程,其他進程處于非運行狀態(tài)椿每。

一個車間里伊者,可以有很多工人。他們協(xié)同完成一個任務间护。

線程就好比車間里的工人删壮。一個進程可以包括多個線程。

車間的空間是工人們共享的兑牡,比如許多房間是每個工人都可以進出的。這象征一個進程的內(nèi)存空間是共享的税灌,每個線程都可以使用這些共享內(nèi)存均函。

可是亿虽,每間房間的大小不同,有些房間最多只能容納一個人苞也,比如廁所洛勉。里面有人的時候,其他人就不能進去了如迟。這代表一個線程使用某些共享內(nèi)存時收毫,其他線程必須等它結束,才能使用這一塊內(nèi)存殷勘。

一個防止他人進入的簡單方法此再,就是門口加一把鎖。先到的人鎖上門玲销,后到的人看到上鎖输拇,就在門口排隊,等鎖打開再進去贤斜。這就叫"互斥鎖"(Mutual exclusion策吠,縮寫 Mutex),防止多個線程同時讀寫某一塊內(nèi)存區(qū)域瘩绒。

還有些房間猴抹,可以同時容納n個人,比如廚房锁荔。也就是說蟀给,如果人數(shù)大于n,多出來的人只能在外面等著堕战。這好比某些內(nèi)存區(qū)域坤溃,只能供給固定數(shù)目的線程使用。

這時的解決方法嘱丢,就是在門口掛n把鑰匙薪介。進去的人就取一把鑰匙,出來時再把鑰匙掛回原處越驻。后到的人發(fā)現(xiàn)鑰匙架空了汁政,就知道必須在門口排隊等著了。這種做法叫做"信號量"(Semaphore)缀旁,用來保證多個線程不會互相沖突记劈。

不難看出,mutex是semaphore的一種特殊情況(n=1時)并巍。也就是說目木,完全可以用后者替代前者。但是懊渡,因為mutex較為簡單刽射,且效率高军拟,所以在必須保證資源獨占的情況下,還是采用這種設計誓禁。

2懈息、多線程與多進程

從上面關于線程和進程的的通俗解釋來看,多線程和多進程的含義如下:
多進程:允許多個任務同時進行
多線程:允許單個任務分成不同的部分運行

3摹恰、Python多線程編程

3.1 單線程

在好些年前的MS-DOS時代辫继,操作系統(tǒng)處理問題都是單任務的,我想做聽音樂和看電影兩件事兒俗慈,那么一定要先排一下順序姑宽。

# coding=UTF-8
from time import ctime,sleep


def music():
    for i in range(2):
        print 'I was listening to music. %s' %ctime()
        sleep(1)

def movie():
    for i in range(2):
        print 'I was at the movies! %s' %ctime()
        sleep(5)

if __name__ == '__main__':
    music()
    movie()
    print 'all over %s' %ctime()

我們先聽了一首音樂,通過for循環(huán)來控制音樂的播放了兩次姜盈,每首音樂播放需要1秒鐘低千,sleep()來控制音樂播放的時長。接著我們又看了一場電影馏颂,每一場電影需要5秒鐘示血,因為太好看了,所以我也通過for循環(huán)看兩遍救拉。在整個休閑娛樂活動結束后难审,我通過print "all over %s" %ctime() 看了一下當前時間,差不多該睡覺了亿絮。
運行結果:

I was listening to music. Mon Aug 21 15:32:18 2017
I was listening to music. Mon Aug 21 15:32:19 2017
I was at the movies! Mon Aug 21 15:32:20 2017
I was at the movies! Mon Aug 21 15:32:25 2017
all over Mon Aug 21 15:32:30 2017

其實告喊,music()和movie()更應該被看作是音樂和視頻播放器,至于要播放什么歌曲和視頻應該由我們使用時決定派昧。所以黔姜,我們對上面代碼做了改造:

# coding=UTF-8

import threading
from time import ctime,sleep

def music(func):
    for i in range(2):
        print ("I was listening to %s. %s" %(func,ctime()))
        sleep(1)

def movie(func):
    for i in range(2):
        print ("I was at the %s ! %s" %(func,ctime()))
        sleep(5)

if __name__ == '__main__':
    music(u'愛情買賣')
    movie(u'阿凡達')
    print("all over %s" %ctime())

運行結果:

I was listening to 愛情買賣. Mon Aug 21 15:38:55 2017
I was listening to 愛情買賣. Mon Aug 21 15:38:56 2017
I was at the 阿凡達 ! Mon Aug 21 15:38:57 2017
I was at the 阿凡達 ! Mon Aug 21 15:39:02 2017
all over Mon Aug 21 15:39:07 2017

3.2 多線程

Python3 通過兩個標準庫 _thread (python2中是thread模塊)和 threading 提供對線程的支持。
_thread 提供了低級別的蒂萎、原始的線程以及一個簡單的鎖秆吵,它相比于 threading 模塊的功能還是比較有限的。

3.2.1使用_thread模塊

調(diào)用_thread模塊中的start_new_thread()函數(shù)來產(chǎn)生新線程五慈。
先用一個實例感受一下:

# coding=UTF-8

import thread
import time


# 為線程定義一個函數(shù)
def print_time(threadName, delay):
    count = 0
    while count < 5:
        time.sleep(delay)
        count += 1
        print("%s: %s" % (threadName, time.ctime(time.time())))


# 創(chuàng)建兩個線程
try:
    thread.start_new_thread(print_time, ("Thread-1", 2,))
    thread.start_new_thread(print_time, ("Thread-2", 4,))
except:
    print ("Error: unable to start thread")

while 1:
    pass

print ("Main Finished")

代碼輸出為:

Thread-1: Mon Aug 21 15:49:00 2017
Thread-2: Mon Aug 21 15:49:02 2017
Thread-1: Mon Aug 21 15:49:02 2017
Thread-1: Mon Aug 21 15:49:04 2017
Thread-2: Mon Aug 21 15:49:06 2017
Thread-1: Mon Aug 21 15:49:06 2017
Thread-1: Mon Aug 21 15:49:08 2017
Thread-2: Mon Aug 21 15:49:10 2017
Thread-2: Mon Aug 21 15:49:14 2017
Thread-2: Mon Aug 21 15:49:18 2017

注意到纳寂,在主線程寫了:

while 1:
   pass

這是讓主線程一直在等待.
如果去掉上面兩行,那就直接輸出并結束程序執(zhí)行:

"Main Finished"
3.2.2使用threading模塊

threading 模塊除了包含 thread 模塊中的所有方法外泻拦,還提供的其他方法:
threading.currentThread(): 返回當前的線程變量毙芜。
threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動后争拐、結束前腋粥,不包括啟動前和終止后的線程。
threading.activeCount(): 返回正在運行的線程數(shù)量,與len(threading.enumerate())有相同的結果灯抛。
除了使用方法外金赦,線程模塊同樣提供了Thread類來處理線程,Thread類提供了以下方法:
run(): 用以表示線程活動的方法对嚼。
start():啟動線程活動。
join([time]): 等待至線程中止绳慎。這阻塞調(diào)用線程直至線程的join() 方法被調(diào)用中止-正常退出或者拋出未處理的異常-或者是可選的超時發(fā)生纵竖。
isAlive(): 返回線程是否活動的。
getName(): 返回線程名杏愤。
setName(): 設置線程名靡砌。

直接創(chuàng)建線程

接上面的聽音樂和看電影的例子,我們可以直接使用threading.Thread 創(chuàng)建線程珊楼,并指定執(zhí)行的方法以及傳遞的參數(shù):

# coding=UTF-8

import threading
from time import ctime, sleep

def music(func):
    for i in range(2):
        print ("I was listening to %s. %s" %(func,ctime()))
        sleep(1)

def movie(func):
    for i in range(2):
        print ("I was at the %s! %s" %(func,ctime()))
        sleep(5)

threads = []
t1 = threading.Thread(target=music,args=(u'愛情買賣',))
threads.append(t1)
t2 = threading.Thread(target=movie,args=(u'阿凡達',))
threads.append(t2)

if __name__ == '__main__':
    for t in threads:
        t.start()

    print ("all over %s" % ctime())

結果輸出為:

I was listening to 愛情買賣. Tue Aug 22 10:41:33 2017
 all over Tue Aug 22 10:41:33 2017
I was at the 阿凡達! Tue Aug 22 10:41:33 2017
I was listening to 愛情買賣. Tue Aug 22 10:41:34 2017
I was at the 阿凡達! Tue Aug 22 10:41:38 2017

構造線程類

我們也可以通過直接從 threading.Thread 繼承創(chuàng)建一個新的子類通殃,并實例化后調(diào)用 start() 方法啟動新線程,即它調(diào)用了線程的 run() 方法:

# coding=UTF-8

import threading
import time

exitFlag = 0


class myThread(threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter

    def run(self):
        print ("開始線程:" + self.name)
        print_time(self.name, self.counter, 5)
        print ("結束線程:" + self.name)


def print_time(threadName, delay, counter):
    while counter:
        if exitFlag:
            threadName.exit()
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1


# 創(chuàng)建新線程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 開始新線程
thread1.start()
thread2.start()
print ("退出主線程")

結果輸出為:

開始線程:Thread-1
開始線程:Thread-2
退出主線程
Thread-1: Tue Aug 22 10:52:55 2017
Thread-2: Tue Aug 22 10:52:56 2017
Thread-1: Tue Aug 22 10:52:56 2017
Thread-1: Tue Aug 22 10:52:57 2017
Thread-2: Tue Aug 22 10:52:58 2017
Thread-1: Tue Aug 22 10:52:58 2017
Thread-1: Tue Aug 22 10:52:59 2017
結束線程:Thread-1
Thread-2: Tue Aug 22 10:53:00 2017
Thread-2: Tue Aug 22 10:53:02 2017
Thread-2: Tue Aug 22 10:53:04 2017
結束線程:Thread-2

從結果可以看到厕宗,為什么我們開啟了兩個線程之后画舌,主線程立即退出了?因為我們沒有使用join方法已慢,對于主線程來說曲聂,thread1和thread2是子線程,使用join方法佑惠,會讓主線程等待子線程執(zhí)行解說再繼續(xù)執(zhí)行朋腋。

join()方法

我們修改一下代碼:

# coding=UTF-8

import threading
import time

exitFlag = 0


class myThread(threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter

    def run(self):
        print ("開始線程:" + self.name)
        print_time(self.name, self.counter, 5)
        print ("退出線程:" + self.name)


def print_time(threadName, delay, counter):
    while counter:
        if exitFlag:
            threadName.exit()
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1


# 創(chuàng)建新線程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 開啟新線程
thread1.start()
thread2.start()
thread1.join()
thread2.join()

print("退出主線程")

結果就變?yōu)椋?/p>

開始線程:Thread-1
開始線程:Thread-2
Thread-1: Tue Aug 22 11:45:53 2017
Thread-2: Tue Aug 22 11:45:54 2017
Thread-1: Tue Aug 22 11:45:54 2017
Thread-1: Tue Aug 22 11:45:55 2017
Thread-2: Tue Aug 22 11:45:56 2017
Thread-1: Tue Aug 22 11:45:56 2017
Thread-1: Tue Aug 22 11:45:57 2017
退出線程:Thread-1
Thread-2: Tue Aug 22 11:45:58 2017
Thread-2: Tue Aug 22 11:46:00 2017
Thread-2: Tue Aug 22 11:46:02 2017
退出線程:Thread-2
退出主線程

可以看到 退出主線程 在最后才被打印出來。

setDaemon()方法

有一個方法常常拿來與join方法做比較膜楷,那就是setDaemon()方法旭咽。我們首先來看一下setDaemon()方法的使用效果:

# coding=UTF-8

import threading
import time

exitFlag = 0

class myThread(threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter

    def run(self):
        print ("開始線程:" + self.name)
        print_time(self.name, self.counter, 5)
        print ("退出線程:" + self.name)


def print_time(threadName, delay, counter):
    while counter:
        if exitFlag:
            threadName.exit()
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1


# 創(chuàng)建新線程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 開啟新線程
thread1.setDaemon(True)
thread2.setDaemon(True)
thread1.start()
thread2.start()

print ("退出主線程")

結果輸出為:

開始線程:Thread-1
開始線程:Thread-2
退出主線程

可以看到,在主線程結束之后赌厅,程序就終止了穷绵,也就是說兩個子線程也被終止了,這就是setDaemon方法的作用察蹲。主線程A中请垛,創(chuàng)建了子線程B,并且在主線程A中調(diào)用了B.setDaemon(),這個的意思是洽议,把主線程A設置為守護線程宗收,這時候,要是主線程A執(zhí)行結束了亚兄,就不管子線程B是否完成,一并和主線程A退出.這就是setDaemon方法的含義混稽,這基本和join是相反的。此外,還有個要特別注意的:必須在start() 方法調(diào)用之前設置匈勋,如果不設置為守護線程礼旅,程序會被無限掛起。

兩個疑問

我們剛才介紹了兩種使用多線程的方式洽洁,一種是直接調(diào)用threading.Thread 創(chuàng)建線程痘系,另一種是從 threading.Thread 繼承創(chuàng)建一個新的子類,并實例化后調(diào)用 start() 方法啟動進程饿自。學到這里汰翠,我就拋出了兩個疑問,為什么第一種方法中我們可以為不同的線程指定運行的方法昭雌,而第二種我們都運行的是同一個方法复唤,那么它內(nèi)部的實現(xiàn)機制是什么呢?第二個疑問是烛卧,第二種方法中佛纫,我們沒有實例化start()方法,那么run和start這兩個方法的聯(lián)系是什么呢总放?
首先呈宇,start方法和run方法的關系如下:用start方法來啟動線程,真正實現(xiàn)了多線程運行间聊,這時無需等待run方法體代碼執(zhí)行完畢而直接繼續(xù)執(zhí)行下面的代碼攒盈。通過調(diào)用Thread類的start()方法來啟動一個線程,這時此線程處于就緒(可運行)狀態(tài)哎榴,并沒有運行型豁,一旦得到cpu時間片,就開始執(zhí)行run()方法尚蝌,這里方法 run()稱為線程體迎变,它包含了要執(zhí)行的這個線程的內(nèi)容,Run方法運行結束飘言,此線程隨即終止衣形。

而run()方法的源碼如下,可以看到姿鸿,如果我們指定了target即線程執(zhí)行的函數(shù)的話谆吴,run方法可以轉而調(diào)用那個函數(shù),如果沒有的話苛预,將不執(zhí)行句狼,而我們在自定義的Thread類里面重寫了這個run 方法,所以程序會執(zhí)行這一段热某。

def run(self):
"""Method representing the thread's activity.

You may override this method in a subclass. The standard run() method
invokes the callable object passed to the object's constructor as the
target argument, if any, with sequential and keyword arguments taken
from the args and kwargs arguments, respectively.

"""
try:
    if self._target:
        self._target(*self._args, **self._kwargs)
finally:
    # Avoid a refcycle if the thread is running a function with
    # an argument that has a member that points to the thread.
    del self._target, self._args, self._kwargs

線程同步

如果多個線程共同對某個數(shù)據(jù)修改腻菇,則可能出現(xiàn)不可預料的結果胳螟,為了保證數(shù)據(jù)的正確性,需要對多個線程進行同步筹吐。
使用 Thread 對象的 Lock 和 Rlock 可以實現(xiàn)簡單的線程同步糖耸,這兩個對象都有 acquire 方法和 release 方法,對于那些需要每次只允許一個線程操作的數(shù)據(jù)丘薛,可以將其操作放到 acquire 和 release 方法之間嘉竟。如下:
多線程的優(yōu)勢在于可以同時運行多個任務(至少感覺起來是這樣)。但是當線程需要共享數(shù)據(jù)時洋侨,可能存在數(shù)據(jù)不同步的問題周拐。
考慮這樣一種情況:一個列表里所有元素都是0,線程"set"從后向前把所有元素改成1凰兑,而線程"print"負責從前往后讀取列表并打印。
那么审丘,可能線程"set"開始改的時候吏够,線程"print"便來打印列表了,輸出就成了一半0一半1滩报,這就是數(shù)據(jù)的不同步锅知。為了避免這種情況,引入了鎖的概念脓钾。
鎖有兩種狀態(tài)——鎖定和未鎖定售睹。每當一個線程比如"set"要訪問共享數(shù)據(jù)時,必須先獲得鎖定可训;如果已經(jīng)有別的線程比如"print"獲得鎖定了昌妹,那么就讓線程"set"暫停,也就是同步阻塞握截;等到線程"print"訪問完畢飞崖,釋放鎖以后,再讓線程"set"繼續(xù)谨胞。
經(jīng)過這樣的處理固歪,打印列表時要么全部輸出0,要么全部輸出1胯努,不會再出現(xiàn)一半0一半1的尷尬場面牢裳。

實例:

# coding=UTF-8

import threading
import time


class myThread(threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter

    def run(self):
        print ("開啟線程:" + self.name)
        # 獲取鎖,用于線程同步
        threadLock.acquire()
        print_time(self.name, self.counter, 3)
        # 釋放鎖叶沛,開啟下一個線程
        threadLock.release()


def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1


threadLock = threading.Lock()
threads = []

# 創(chuàng)建新進程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 開啟新進程
thread1.start()
thread2.start()

# 添加線程到線程列表
threads.append(thread1)
threads.append(thread2)

# 等待所有線程完成
for t in threads:
    t.join()
print("退出主線程")

輸出為:

開啟線程:Thread-1
開啟線程:Thread-2
Thread-1: Tue Aug 22 15:08:20 2017
Thread-1: Tue Aug 22 15:08:21 2017
Thread-1: Tue Aug 22 15:08:22 2017
Thread-2: Tue Aug 22 15:08:24 2017
Thread-2: Tue Aug 22 15:08:26 2017
Thread-2: Tue Aug 22 15:08:28 2017
退出主線程

線程優(yōu)先級隊列( Queue)

Python 的 Queue 模塊中提供了同步的蒲讯、線程安全的隊列類,包括FIFO(先入先出)隊列Queue恬汁,LIFO(后入先出)隊列LifoQueue伶椿,和優(yōu)先級隊列 PriorityQueue辜伟。
這些隊列都實現(xiàn)了鎖原語,能夠在多線程中直接使用脊另,可以使用隊列來實現(xiàn)線程間的同步导狡。
Queue 模塊中的常用方法:
Queue.qsize() 返回隊列的大小
Queue.empty() 如果隊列為空,返回True,反之False
Queue.full() 如果隊列滿了偎痛,返回True,反之False
Queue.full 與 maxsize 大小對應
Queue.get([block[, timeout]])獲取隊列旱捧,timeout等待時間
Queue.get_nowait() 相當Queue.get(False)
Queue.put(item) 寫入隊列,timeout等待時間
Queue.put_nowait(item) 相當Queue.put(item, False)
Queue.task_done() 在完成一項工作之后踩麦,Queue.task_done()函數(shù)向任務已經(jīng)完成的隊列發(fā)送一個信號
Queue.join() 實際上意味著等到隊列為空枚赡,再執(zhí)行別的操作

# coding=UTF-8

import threading
import time
import Queue

exitFlag = 0


class myThread(threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q

    def run(self):
        print("開啟線程:" + self.name)
        process_data(self.name, self.q)
        print("退出線程:" + self.name)


def process_data(threadName, q):
    while not exitFlag:
        queueLock.acquire()
        if not workQueue.empty():
            data = q.get()
            queueLock.release()
            print ("%s processing %s" % (threadName, data))
        else:
            queueLock.release()
        time.sleep(1)


threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1

# 創(chuàng)建新線程
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1

# 填充隊列
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

# 等待隊列為空
while not workQueue.empty():
    pass

# 通知線程是否退出
exitFlag = 1

# 等待所有線程完成
for t in threads:
    t.join()

print("退出主線程")

上面的代碼每次執(zhí)行的結果是不一樣的,取決于哪個進程先獲得鎖谓谦,一次運行的輸出如下:

開啟線程:Thread-1
開啟線程:Thread-2
開啟線程:Thread-3
Thread-2 processing One
Thread-1 processing Two
Thread-3 processing Three
Thread-2 processing Four
Thread-1 processing Five
退出線程:Thread-3
退出線程:Thread-2
退出線程:Thread-1
退出主線程

參考文章

進程與線程的一個簡單解釋:
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
多線程就這么簡單:
http://www.cnblogs.com/fnng/p/3670789.html

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贫橙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子反粥,更是在濱河造成了極大的恐慌卢肃,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件才顿,死亡現(xiàn)場離奇詭異莫湘,居然都是意外死亡,警方通過查閱死者的電腦和手機郑气,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門幅垮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尾组,你說我怎么就攤上這事忙芒。” “怎么了演怎?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵匕争,是天一觀的道長。 經(jīng)常有香客問我爷耀,道長甘桑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任歹叮,我火速辦了婚禮跑杭,結果婚禮上,老公的妹妹穿的比我還像新娘咆耿。我一直安慰自己德谅,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布萨螺。 她就那樣靜靜地躺著窄做,像睡著了一般愧驱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上椭盏,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天组砚,我揣著相機與錄音,去河邊找鬼掏颊。 笑死糟红,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的乌叶。 我是一名探鬼主播盆偿,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼准浴!你這毒婦竟也來了事扭?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤乐横,失蹤者是張志新(化名)和其女友劉穎句旱,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晰奖,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年腥泥,在試婚紗的時候發(fā)現(xiàn)自己被綠了匾南。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛔外,死狀恐怖蛆楞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情夹厌,我是刑警寧澤豹爹,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站矛纹,受9級特大地震影響臂聋,放射性物質發(fā)生泄漏。R本人自食惡果不足惜或南,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一孩等、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧采够,春花似錦肄方、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虹茶。三九已至,卻和暖如春隅要,著一層夾襖步出監(jiān)牢的瞬間蝴罪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工拾徙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洲炊,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓尼啡,卻偏偏與公主長得像暂衡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子崖瞭,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1狂巢、線程和進程 計算機的核心是CPU,它承擔了所有的計算任務书聚。它就像一座工廠唧领,時刻在運行。 假定工廠的電力有限雌续,一...
    文哥的學習日記閱讀 14,339評論 0 9
  • 來源:數(shù)據(jù)分析網(wǎng)Threading 模塊從 Python 1.5.2 版開始出現(xiàn)斩个,用于增強底層的多線程模塊 thr...
    PyChina閱讀 1,737評論 0 5
  • 線程 引言&動機 考慮一下這個場景,我們有10000條數(shù)據(jù)需要處理驯杜,處理每條數(shù)據(jù)需要花費1秒受啥,但讀取數(shù)據(jù)只需要0....
    不浪漫的浪漫_ea03閱讀 358評論 0 0
  • 引言&動機 考慮一下這個場景,我們有10000條數(shù)據(jù)需要處理鸽心,處理每條數(shù)據(jù)需要花費1秒滚局,但讀取數(shù)據(jù)只需要0.1秒,...
    chen_000閱讀 501評論 0 0
  • 1. 區(qū)分重載方法 規(guī)則很簡單顽频,每個重載的方法都必須有一個獨一無二的參數(shù)類型列表藤肢。以返回值來區(qū)分重載方法是行不通的...
    Megamind_China閱讀 340評論 0 0