一 多线程实现

线程模块

- 多线程主要的内容:直接进行多线程操作,线程同步,带队列的多线程;

Python3 通过两个标准库 _thread 和 threading 提供对线程的支持。

_thread 提供了低级别的、原始的线程以及一个简单的锁,它相比于 threading 模块的功能还是比较有限的。

threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:

threading.currentThread(): 返回当前的线程变量。

threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。

threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:

run(): 用以表示线程活动的方法。

start():启动线程活动。

join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。

isAlive(): 返回线程是否活动的。

getName(): 返回线程名。

setName(): 设置线程名。

1. 最简单的多线程

import threading

import time

deftarget():print("the current threading %s is runing"

%(threading.current_thread().name))

time.sleep(1)print("the current threading %s is ended"%(threading.current_thread().name))print("the current threading %s is runing"%(threading.current_thread().name))## 属于线程t的部分

t = threading.Thread(target=target)

t.start()## 属于线程t的部分

t.join() #join是阻塞当前线程(此处的当前线程时主线程) 主线程直到Thread-1结束之后才结束

print("the current threading %s is ended"%(threading.current_thread().name))

2.通过继承threading.Thread定义子类创建多线程

使用Threading模块创建线程,直接从threading.Thread继承,然后重写init方法和run方法:

importthreadingimporttimeclass myThread(threading.Thread): #继承父类threading.Thread

def __init__(self, threadID, name, counter):

threading.Thread.__init__(self)

self.threadID=threadID

self.name=name

self.counter=counterdef run(self): #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数

print("Starting" +self.name)

print_time(self.name, self.counter,5)print("Exiting" +self.name)defprint_time(threadName, delay, counter):whilecounter:

time.sleep(delay)print("%s process at: %s" %(threadName, time.ctime(time.time())))

counter-= 1

#创建新线程

thread1 = myThread(1, "Thread-1", 1)

thread2= myThread(2, "Thread-2", 2)#开启线程

thread1.start()

thread2.start()#等待线程结束

thread1.join()

thread2.join()print("Exiting Main Thread")

3. 主线程退出,进程等待所有子线程执行完毕后才结束

进程启动后会默认产生一个主线程,默认情况下主线程创建的子线程都不是守护线程(setDaemon(False))。

因此主线程结束后,子线程会继续执行,进程会等待所有子线程执行完毕后才结束

所有线程共享一个终端输出(线程所属进程的终端)

importthreadingimporttimedefchild_thread1():for i in range(100):

time.sleep(1)print('child_thread1_running...')defparent_thread():print('parent_thread_running...')

thread1= threading.Thread(target=child_thread1)

thread1.start()print('parent_thread_exit...')if __name__ == "__main__":

parent_thread()

输出为:

parent_thread_running...

parent_thread_exit...

child_thread1_running...

child_thread1_running...

child_thread1_running...

child_thread1_running...

...

可见父线程结束后,子线程仍在运行,此时结束进程,子线程才会被终止

4.主线程结束后进程不等待守护线程完成,程序立即结束

当设置一个线程为守护线程时,此线程所属进程不会等待此线程运行结束,进程将立即结束

importthreadingimporttimedefchild_thread1():for i in range(100):

time.sleep(1)print('child_thread1_running...')defchild_thread2():for i in range(5):

time.sleep(1)print('child_thread2_running...')defparent_thread():print('parent_thread_running...')

thread1= threading.Thread(target=child_thread1)

thread2= threading.Thread(target=child_thread2)

thread1.setDaemon(True)

thread1.start()

thread2.start()print('parent_thread_exit...')if __name__ == "__main__":

parent_thread()

输出:

parent_thread_running...

parent_thread_exit...

child_thread1_running...child_thread2_running...

child_thread1_running...child_thread2_running...

child_thread1_running...child_thread2_running...

child_thread1_running...child_thread2_running...

child_thread2_running...child_thread1_running...

Process finished with exit code 0

thread1是守护线程,thread2非守护线程,因此,进程会等待thread2完成后结束,而不会等待thread1完成

注意:子线程会继承父线程中daemon的值,即守护线程开启的子线程仍是守护线程

5.主线程等待子线程完成后结束

在线程A中使用B.join()表示线程A在调用join()处被阻塞,且要等待线程B的完成才能继续执行

importthreadingimporttimedefchild_thread1():for i in range(10):

time.sleep(1)print('child_thread1_running...')defchild_thread2():for i in range(5):

time.sleep(1)print('child_thread2_running...')defparent_thread():print('parent_thread_running...')

thread1= threading.Thread(target=child_thread1)

thread2= threading.Thread(target=child_thread2)

thread1.setDaemon(True)

thread2.setDaemon(True)

thread1.start()

thread2.start()

thread2.join()1/0

thread1.join()print('parent_thread_exit...')if __name__ == "__main__":

parent_thread()

输出:

parent_thread_running...

child_thread1_running...

child_thread2_running...

child_thread1_running...

child_thread2_running...

child_thread1_running...

child_thread2_running...

child_thread1_running...

child_thread2_running...

child_thread1_running...

child_thread2_running...

Traceback (most recent call last):

File"E:/test_thread.py", line 31, in parent_thread()

File"E:/test_thread.py", line 25, inparent_thread1/0

ZeroDivisionError: integer divisionor modulo by zero

主线程在执行到thread2.join()时被阻塞,等待thread2结束后才会执行下一句

1/0 会使主线程报错退出,且thread1设置了daemon=True,因此主线程意外退出时thread1也会立即结束。thread1.join()没有被主线程执行

6.子线程间的执行顺序

6.1 默认状态下

importthreadingimporttimedefchild_thread1():for i in range(50):

time.sleep(1)print('child_thread1_running...')defchild_thread2():for i in range(50):

time.sleep(1)print('child_thread2_running...')defparent_thread():print('parent_thread_running...')

thread1= threading.Thread(target=child_thread1)

thread2= threading.Thread(target=child_thread2)

thread1.start()

thread2.start()

#

#print('parent_thread_exit...')if __name__ == "__main__":

parent_thread()

结果

parent_thread_running...

parent_thread_exit...

child_thread2_running...

child_thread1_running...

child_thread1_running...child_thread2_running...

child_thread1_running...child_thread2_running...

child_thread2_running...

child_thread1_running...

child_thread1_running...

child_thread2_running...

child_thread1_running...child_thread2_running...

child_thread1_running...child_thread2_running...

child_thread1_running...child_thread2_running...

child_thread1_running...

child_thread2_running...

child_thread1_running...

child_thread2_running...

child_thread2_running...

child_thread1_running...

child_thread1_running...child_thread2_running...

child_thread1_running...child_thread2_running...

child_thread2_running...

child_thread1_running...

child_thread1_running...

child_thread2_running...

child_thread2_running...

child_thread1_running...

child_thread2_running...

child_thread1_running...

child_thread1_running...

child_thread2_running...

child_thread1_running...child_thread2_running...

child_thread2_running...child_thread1_running...

child_thread1_running...

child_thread2_running...

child_thread2_running...

child_thread1_running...

child_thread1_running...

child_thread2_running...

child_thread2_running...

child_thread1_running...

...

...

[Finishedin 50.1s]

默认状态下,子线程的工作顺序随机,没有明显先后顺序,主线程比子线程早结束

6.2 使用join

child_thread1先执行,完成后,再执行child_thread2,child_thread2执行完成后,主线程再执行print('parent_thread_exit...')后退出

importthreadingimporttimedefchild_thread1():for i in range(50):

time.sleep(1)print('child_thread1_running...')defchild_thread2():for i in range(50):

time.sleep(1)print('child_thread2_running...')defparent_thread():print('parent_thread_running...')

thread1= threading.Thread(target=child_thread1)

thread2= threading.Thread(target=child_thread2)

thread1.start()

thread1.join()

thread2.start()

thread2.join()print('parent_thread_exit...')if __name__ == "__main__":

parent_thread()

结果

parent_thread_running...

child_thread1_running...

...

child_thread1_running...

child_thread1_running...

child_thread2_running...

child_thread2_running...

......

child_thread2_running...

parent_thread_exit...

[Finishedin 100.2s]

child_thread2先执行,完成后,再执行child_thread1,child_thread2执行完成后,主线程再执行print('parent_thread_exit...')后退出

importthreadingimporttimedefchild_thread1():for i in range(50):

time.sleep(1)print('child_thread1_running...')defchild_thread2():for i in range(50):

time.sleep(1)print('child_thread2_running...')defparent_thread():print('parent_thread_running...')

thread1= threading.Thread(target=child_thread1)

thread2= threading.Thread(target=child_thread2)

thread2.start()

thread2.join()

thread1.start()

thread1.join()print('parent_thread_exit...')if __name__ == "__main__":

parent_thread()

child_thread1与child_thread2没有执行顺序,主线程再执行print('parent_thread_exit...')后退出

importthreadingimporttimedefchild_thread1():for i in range(50):

time.sleep(1)print('child_thread1_running...')defchild_thread2():for i in range(50):

time.sleep(1)print('child_thread2_running...')defparent_thread():print('parent_thread_running...')

thread1= threading.Thread(target=child_thread1)

thread2= threading.Thread(target=child_thread2)

thread2.start()

thread1.start()

thread2.join()

thread1.join()print('parent_thread_exit...')if __name__ == "__main__":

parent_thread()

7.线程间的通信

多线程共享全局变量

线程时进程的执行单元,进程时系统分配资源的最小执行单位,所以在同一个进程中的多线程是共享资源的

7.1 多线程共享全局变量

importthreading

importtime

#g_num = 100#def work1():#global g_num#for i in range(3):#g_num+=1#print('in work1 g_num is : %d' % g_num)#

#def work2():#global g_num#print('in work2 g_num is : %d' % g_num)#

#if __name__ == '__main__':#t1 = threading.Thread(target=work1)#t1.start()#time.sleep(1)#t2=threading.Thread(target=work2)#t2.start()

7.2 互斥锁(Lock)

由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以出现了线程锁,即同一时刻允许一个线程执行操作。线程锁用于锁定资源,可以定义多个锁,像下面的代码,当需要独占某一个资源时,任何一个锁都可以锁定这个资源,就好比你用不同的锁都可以把这个相同的门锁住一样。

由于线程之间是进行随机调度的,如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期,

我们因此也称为“线程不安全”。为了防止上面情况的发生,就出现了互斥锁(Lock)

importtime, threading

count=0 #声明全局变量

lock=threading.Lock() #申请一把锁

deflajifenlei():global count #引用全局变量

lock.acquire() #加锁

try:

count+=1

exceptException as e:pass

finally:

lock.release()#释放锁

time.sleep(1)print(count)for i in range(10):

th= threading.Thread(target=lajifenlei,) #声明线程数

th.start() #启动线程

while threading.activeCount()!=1:pass

另一种类似打开和关闭文件的with方法,自动开关锁

importtime, threading

count=0 #声明全局变量

lock=threading.Lock() #申请一把锁

deflajifenlei():global count #引用全局变量

with lock:#with模块自动加锁及解锁

count+=1time.sleep(1)print(count)for i in range(10):

th= threading.Thread(target=lajifenlei,) #声明线程数

th.start() #启动线程

while threading.activeCount()!=1:pass

7.3 线程同步队列queue

Python的queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。

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()函数向任务已经完成的队列发送一个信号

queue.join() 实际上意味着等到队列为空,再执行别的操作

#coding: utf-8

from queue importQueue#Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递

deftest_queue():

q=Queue(10)for i in range(5):

q.put(i)while notq.empty():print(q.get())deftest_LifoQueue():importqueue#queue.LifoQueue() #后进先出->堆栈

q = queue.LifoQueue(3)

q.put(1)

q.put(2)

q.put(3)print(q.get())print(q.get())print(q.get())deftest_PriorityQueue():importqueue#queue.PriorityQueue() #优先级

q = queue.PriorityQueue(3) #优先级,优先级用数字表示,数字越小优先级越高

q.put((10, 'a'))

q.put((-1, 'b'))

q.put((100, 'c'))print(q.get())print(q.get())print(q.get())#Python queue队列,实现并发,在网站多线程推荐最后也一个例子,比这货简单,但是不够规范

from queue import Queue #Queue在3.x中改成了queue

importrandomimportthreadingimporttimefrom threading importThreadclassProducer(threading.Thread):"""Producer thread 制作线程"""

def __init__(self, t_name, queue): #传入线程名、实例化队列

threading.Thread.__init__(self, name=t_name) #t_name即是threadName

self.data =queue"""run方法 和start方法:

它们都是从Thread继承而来的,run()方法将在线程开启后执行,

可以把相关的逻辑写到run方法中(通常把run方法称为活动[Activity]);

start()方法用于启动线程。"""

defrun(self):for i in range(5): #生成0-4五条队列

print("%s: %s is producing %d to the queue!" % (time.ctime(), self.getName(), i)) #当前时间t生成编号d并加入队列

self.data.put(i) #写入队列编号

time.sleep(random.randrange(10) / 5) #随机休息一会

print("%s: %s producing finished!" % (time.ctime(), self.getName)) #编号d队列完成制作

classConsumer(threading.Thread):"""Consumer thread 消费线程,感觉来源于COOKBOOK"""

def __init__(self, t_name, queue):

threading.Thread.__init__(self, name=t_name)

self.data=queuedefrun(self):for i in range(5):

val=self.data.get()print("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(), self.getName(), val)) #编号d队列已经被消费

time.sleep(random.randrange(10))print("%s: %s consuming finished!" % (time.ctime(), self.getName())) #编号d队列完成消费

defmain():"""Main thread 主线程"""queue= Queue() #队列实例化

producer = Producer('Pro.', queue) #调用对象,并传如参数线程名、实例化队列

consumer = Consumer('Con.', queue) #同上,在制造的同时进行消费

producer.start() #开始制造

consumer.start() #开始消费

"""join()的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

join()方法的位置是在for循环外的,也就是说必须等待for循环里的两个进程都结束后,才去执行主进程。"""producer.join()

consumer.join()print('All threads terminate!')if __name__=="__main__":

test_queue()print("=====后进先出=====")

test_LifoQueue()print("=====优先级======")

test_PriorityQueue()

main()

7.4 threading.local

二 线程池实现

ThreadPoolExecutor 基础

from concurrent.futures importThreadPoolExecutorimporttime#参数times用来模拟网络请求的时间

defget_html(times):

time.sleep(times)print("get page {}s finished".format(times))returntimes

executor= ThreadPoolExecutor(max_workers=2)#通过submit函数提交执行的函数到线程池中,submit函数立即返回,不阻塞

task1 = executor.submit(get_html, (3))

task2= executor.submit(get_html, (2))#done方法用于判定某个任务是否完成

print(task1.done())#cancel方法用于取消某个任务,该任务没有放入线程池中才能取消成功

print(task2.cancel())

time.sleep(4)print(task1.done())#result方法可以获取task的执行结果

print(task1.result())#执行结果#False # 表明task1未执行完成#False # 表明task2取消失败,因为已经放入了线程池中#get page 2s finished#get page 3s finished#True # 由于在get page 3s finished之后才打印,所以此时task1必然完成了#3 # 得到task1的任务返回值

ThreadPoolExecutor构造实例的时候,传入max_workers参数来设置线程池中最多能同时运行的线程数目。

使用submit函数来提交线程需要执行的任务(函数名和参数)到线程池中,并返回该任务的句柄(类似于文件、画图),注意submit()不是阻塞的,而是立即返回。

通过submit函数返回的任务句柄,能够使用done()方法判断该任务是否结束。上面的例子可以看出,由于任务有2s的延时,在task1提交后立刻判断,task1还未完成,而在延时4s之后判断,task1就完成了。

使用cancel()方法可以取消提交的任务,如果任务已经在线程池中运行了,就取消不了。这个例子中,线程池的大小设置为2,任务已经在运行了,所以取消失败。如果改变线程池的大小为1,那么先提交的是task1,task2还在排队等候,这是时候就可以成功取消。

使用result()方法可以获取任务的返回值。查看内部代码,发现这个方法是阻塞的。

as_completed

上面虽然提供了判断任务是否结束的方法,但是不能在主线程中一直判断啊。有时候我们是得知某个任务结束了,就去获取结果,而不是一直判断每个任务有没有结束。这是就可以使用as_completed方法一次取出所有任务的结果。

from concurrent.futures importThreadPoolExecutor, as_completedimporttime#参数times用来模拟网络请求的时间

defget_html(times):

time.sleep(times)print("get page {}s finished".format(times))returntimes

executor= ThreadPoolExecutor(max_workers=2)

urls= [3, 2, 4] #并不是真的url

all_task = [executor.submit(get_html, (url)) for url inurls]for future inas_completed(all_task):

data=future.result()print("in main: get page {}s success".format(data))#执行结果#get page 2s finished#in main: get page 2s success#get page 3s finished#in main: get page 3s success#get page 4s finished#in main: get page 4s success

as_completed()方法是一个生成器,在没有任务完成的时候,会阻塞,在有某个任务完成的时候,会yield这个任务,就能执行for循环下面的语句,然后继续阻塞住,循环到所有的任务结束。从结果也可以看出,先完成的任务会先通知主线程。

map

除了上面的as_completed方法,还可以使用executor.map方法,但是有一点不同。

from concurrent.futures importThreadPoolExecutorimporttime#参数times用来模拟网络请求的时间

defget_html(times):

time.sleep(times)print("get page {}s finished".format(times))returntimes

executor= ThreadPoolExecutor(max_workers=2)

urls= [3, 2, 4] #并不是真的url

for data inexecutor.map(get_html, urls):print("in main: get page {}s success".format(data))#执行结果#get page 2s finished#get page 3s finished#in main: get page 3s success#in main: get page 2s success#get page 4s finished#in main: get page 4s success

使用map方法,无需提前使用submit方法,map方法与python标准库中的map含义相同,都是将序列中的每个元素都执行同一个函数。上面的代码就是对urls的每个元素都执行get_html函数,并分配各线程池。可以看到执行结果与上面的as_completed方法的结果不同,输出顺序和urls列表的顺序相同,就算2s的任务先执行完成,也会先打印出3s的任务先完成,再打印2s的任务完成。

wait

wait方法可以让主线程阻塞,直到满足设定的要求。

from concurrent.futures importThreadPoolExecutor, wait, ALL_COMPLETED, FIRST_COMPLETEDimporttime#参数times用来模拟网络请求的时间

defget_html(times):

time.sleep(times)print("get page {}s finished".format(times))returntimes

executor= ThreadPoolExecutor(max_workers=2)

urls= [3, 2, 4] #并不是真的url

all_task = [executor.submit(get_html, (url)) for url inurls]

wait(all_task, return_when=ALL_COMPLETED)print("main")#执行结果#get page 2s finished#get page 3s finished#get page 4s finished#main

wait方法接收3个参数,等待的任务序列、超时时间以及等待条件。等待条件return_when默认为ALL_COMPLETED,表明要等待所有的任务都结束。可以看到运行结果中,确实是所有任务都完成了,主线程才打印出main。等待条件还可以设置为FIRST_COMPLETED,表示第一个任务完成就停止等待。

ThreadPoolExecutor 间的通信

三 多线程爬虫实现

实例 抓取b站up主视频评论

1.普通多线程(利用threading的local)

importreimportrequestsimportjsonimportthreadingimportmathimporttime

HEADERS= {#'Accept':"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",

'User-Agent': 'Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; MI 6 Build/NMF26X) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',

}

local=threading.local()#Thread-local state to stored information on locks already acquired

defstart_urls(total_page):#生产者 产生用于消费的urls任务列表

tasks =list()

url= "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn={}&type=1&oid=455312953&sort=2&_=1587372277524"

for i in range(1,total_page+1):

tasks.append(url.format(i))returntasksdefinit_start():#获取评论列表的总页数

url = "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn=1&type=1&oid=455312953&sort=2&_=1587372277524"content=downloader(url)

data=json.loads(content.text)

total_page= math.ceil(int(data['data']['page']['count'])/int(data['data']['page']['size']))print(total_page)returntotal_pagedefdownloader(url):#下载任务

content = requests.get(url,headers=HEADERS)returncontentdefwork(tasks,n):#消费者

whilelen(tasks):

time.sleep(1)try:

local.url=tasks.pop()exceptException as e:print('e',e)continue

print(local.url,threading.current_thread())

data=downloader(local.url)if __name__ == '__main__':

total_page=init_start()

task_urls=start_urls(total_page)for i in range(3):

t= threading.Thread(target=work,args=(task_urls,i))

t.start()

2.线程池运用版本

importreimportrequestsimportjsonimportthreadingimportmathimporttimefrom concurrent.futures importThreadPoolExecutor, as_completed

HEADERS= {#'Accept':"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",

'User-Agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',

}

local=threading.local()#Thread-local state to stored information on locks already acquired

defstart_urls(total_page):#生产者 产生用于消费的urls任务列表

tasks =list()

url= "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn={}&type=1&oid=455312953&sort=2&_=1587372277524"

for i in range(1,total_page+1):

tasks.append(url.format(i))returntasksdefinit_start():#获取评论列表的总页数

url = "https://api.bilibili.com/x/v2/reply?jsonp=jsonp&pn=1&type=1&oid=455312953&sort=2&_=1587372277524"content=downloader(url)

data=json.loads(content.text)

total_page= math.ceil(int(data['data']['page']['count'])/int(data['data']['page']['size']))print(total_page)returntotal_pagedefdownloader(url):#下载任务

content = requests.get(url,headers=HEADERS)print(content.status_code,type(content.status_code))returncontentdefwork(tasks,n):#消费者

whilelen(tasks):

time.sleep(1)try:

local.url=tasks.pop()exceptException as e:print('e',e)continue

print(local.url,threading.current_thread())

data=downloader(local.url)if __name__ == '__main__':

total_page=init_start()

task_urls=start_urls(total_page)

with ThreadPoolExecutor(max_workers=5) as executor:

all_task= [executor.submit(work, task_urls,i) for i in range(3)]for future inas_completed(all_task):

data=future.result()print("in main: get page {}s success".format(data))

python线程池并发爬虫_python 并发专题(二):python线程以及线程池相关以及实现...相关推荐

  1. 视频教程-python项目之学员CRM管理系统开发阶段二-Python

    python项目之学员CRM管理系统开发阶段二 TriAquae开源运维软件创始人,混迹IT运维领域多年,曾就职于松下.国政通.飞信.中金.NOKIA等公司,维护过少至几十台,多至数万台设备的IT系统 ...

  2. python高并发编程_Python——并发编程

    开始说并发编程之前,最好有一定的底层知识积累,这里我把需要的知识总结了一下,如果看下面的有不理解的可以看一下:https://www.cnblogs.com/kuxingseng95/p/941820 ...

  3. python中rim的用法_Python并发开发简介

    Python的并发程序可以使用multiprocessing库.threading库.asyncio库.concurrent.futures库以及selectors库等等协助编写: multiproc ...

  4. python并发之协程_python并发编程之协程

    一 引子 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去 ...

  5. 理解python并发编程_Python并发编程很简单

    上次已经和大家探讨了关于进程和线程的区别和联系相关的东东,今天呢,咱们再次回到 好啦,废话少说,咱们就开始吧! 首先说一下哦,_thread和threading哦,到这可能有朋友会问了,这两个有什么区 ...

  6. 实战Go语言:并发爬虫-欧阳桫-专题视频课程

    实战Go语言:并发爬虫-84人已学习 课程介绍         基于正则表达式和并发技术的整站爬虫项目: 功能包括:通用正则表达式写法.分页爬取.整站爬取: 技术栈:正则表达式.协程并发.HTTP通信 ...

  7. python多久能学会爬虫-上海多久可以学会python

    上海多久可以学会python 来源:教育联展网 编辑:粉色de皮卡丘 发布时间:2018-12-29 上海Python培训班 千锋教育打造人工智能Python工程师 快速咨询 上海Python培训 上 ...

  8. python的起源和发展_Python入门第一课——Python的起源、发展与前景!

    我们在做任何一件事情之前,我们都会通过各种渠道去搜集事情的信息,了解事情的来龙去脉,学习一门编程语言也是如此,只有知根知底,我们才能有明确的方向和目标,以及底气去完成这件事情,今天我带大家来看看Pyt ...

  9. python 路径往上一层_Python常用模块之模块、包介绍和相关语法

    在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很 ...

最新文章

  1. CVPR 2021 比CNN和Transformer更好的Backbone?伯克利谷歌提出BoTNet,精度达84.7%
  2. 十一、Grafana监控系统
  3. Java 使用execute方法执行Sql语句
  4. 验证二叉搜索树的golang实现
  5. 浅谈mtk平台手机通过gprs网络连接pc
  6. CMDB内功心法,助我登上运维之巅
  7. 从0开始学习C#第二天
  8. 微信公众号中视频下载的2种方法
  9. 修复ipad提示无法连接到服务器,修复无法连接到iPad的App Store
  10. js日期格式化函数示例:将日期时间格式化成yyyy-mm-dd hh:ii:ss格式
  11. mysql闪现_css,javascript_点击按钮出现弹窗,弹窗只是闪现了一下,为什么没有一直出现?,css,javascript - phpStudy...
  12. 求大神帮助 百练我爱北大,编程题目
  13. 【学习笔记】利用API进行数据采集或获取
  14. JetPack知识点实战系列十:ExoPlayer进行视频播放的实现
  15. 00截断原理核心分析(重点)
  16. springMVC + Dubbo + zooKeeper超详细 步骤
  17. 噪声种类及Matlab添加噪声
  18. AttributeError: module ‘torch.distributed‘ has no attribute ‘_all_gather_base‘
  19. 怎么用博客来把自己网站“养大”
  20. CNI系列(三)插件实现

热门文章

  1. tensorflow错误记录:tf.concat
  2. 视讯稳定的程序在gpu 那台电脑上没有问题 在自己的电脑上一直报一个错误
  3. 反射机制在JDBC连接中的使用
  4. 在cisco switch上配置MSTP
  5. 随想录(常用的c库)
  6. linux git添加密钥,centos – 无法在Gitlab上创建项目或添加ssh密钥
  7. 广西国际商务职业技术学院官网计算机功课,2019—2020学年秋季学期线上线下混合式教学课程听课情况汇报...
  8. 残差曲线意义_生存曲线(三):统计分析方法这么多,到底选哪个?
  9. linux新硬盘分区及格式化和挂载分区到目录的方法
  10. CF719E. Sasha and Array [线段树维护矩阵]