1/前言

计算机中的CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
假定工厂的电力有限,一次只能供给一个车间。
也就是说,一个车间开工的时候,其他车间都必须停工。
背后的含义就是,单核CPU一次只能运行一个任务(应用程序,进程)。
这就是现在人们想买多核cpu笔记本的原因。
进程就好比工厂的车间,它代表cpu所能处理的单个任务。
任一时刻,单核cpu总是运行一个进程,其他进程处于非运行状态。
cpu在多个进程之间快速切换,给人一种“同时执行”的错觉。
但是如果进程很多,在切换的时候就会造成电脑卡顿。
一个车间里,可以有很多工人,他们协同完成一个任务。(当然也可以只有一个工人)

线程就好比某个车间里的工人,一个进程可以包括多个线程(或只有一个线程)。
一个人也可以完成一件时间,多个人也可以协同完成一件事情。
车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。
这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
可是,每间房间的大小不同,有些房间最多只能容纳一个人。
比如厕所,里面有人的时候,其他人就不能进去了。这代表一个线程在使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
一个防止他人进入的简单方法,就是门口加一把锁。
先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。
这就叫”互斥锁”,防止多个线程同时读写某一块内存区域。
还有些房间,可以同时容纳n个人,比如厨房。
也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
这种情况的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。
后到的人发现钥匙架空了,就知道必须在门口排队等着了。
这种做法叫做”信号量”(Semaphore),用来保证多个线程不会互相冲突。
不难看出,互斥锁是信号量的一种特殊情况(n=1时)。
也就是说,完全可以用后者替代前者。但是,因为mutex(互斥锁)较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。
2/总结
操作系统的设计,因此可以归结为三点:
<1>以多进程形式,允许多个任务同时运行(并行);
<2>以多线程形式,允许单个任务分成不同的部分运行;
<3>提供协调机制,一方面防止进程之间和线程之间产生冲突,
另一方面允许进程之间和线程之间共享资源。
Python获取多线程返回值的两种方式
通过复写Thread类,自定义一个get_result()方法
from threading import Thread
def cal_sum(a,b):
return a+b
class MyThread(Thread):
def __init__(self, func, args):
super(MyThread, self).__init__()
self.func = func
self.args = args
def run(self):
self.result = self.func(*self.args)
def get_result(self):
try:
return self.result
except Exception:
return None
if __name__ == "__main__":
subt1 = MyThread(cal_sum,args=(1,5))
subt2 = MyThread(cal_sum,args=(6,10))
subt1.start()
subt2.start()
subt1.join()
subt2.join()
res1 = subt1.get_result()
res2 = subt2.get_result()
print("主线程开始......")
print(res1 + res2)
print("主线程结束......")
为什么要使用多线程
线程是并发的执行流,与独立的进程相比,一个进程中各个线程之间的隔离程度小,容易切换。
他们共享内存,文件句柄和其他进程应有的状态
因为线程的划分尺度小于进程,使得多线程程序的并发性高,也就是多个线程之间切换。
进程在执行的时候拥有独立的内存单元,而多个线程共享内存,从而极大的提高了程序的运行效率。
主线程,子线程,守护线程
<1>看下面的2个例子,
这里使用setDaemon(True)把所有的子线程都变成了主线程的守护线程,
因此当主线程结束后,子线程也会随之结束。当主线程结束后,整个程序就退出了。
根据最后的输出结果,我们可以看到,当主线程结束,子线程也将立即结束。
import threading
import time
def f(n):
print("task",n)
time.sleep(1)
print(3)
time.sleep(1)
print(2)
time.sleep(1)
print(1)
if __name__ == "__main__":
subt = threading.Thread(target=f,args=("t1",))
subt.setDaemon(True)
subt.start()
print("end")
上面代码的执行结果是:
task,t1
end

<2>主线程等待子线程结束。
为了让守护线程执行结束之后,主线程再结束,我们可以使用join方法,
让主线程等待子线程执行。
import threading
import time
def main(n):
print("task",n)
time.sleep(1)
print(3)
time.sleep(1)
print(2)
time.sleep(1)
print(1)
if __name__ == "__main__":
t = threading.Thread(target=main,args=("t1",))
t.setDaemon(True)
t.start()
print("end")
上面代码的执行结果是
task t1
3
2
1
end
多线程代码实例
<1> 多线程得到返回结果
from threading import Thread
class MyThread(Thread):
def __init__(self,func,args=()):
super(MyThread,self).__init__()
self.func = func
self.args = args
def run(self):
self.result = self.func(*self.args)
def get_result(self):
try:
return self.result
except Exception:
return None
def f(raw_list, n):
for i in range(0, len(raw_list), n):
yield raw_list[i:i + n]
threads_pool = []
for i in temp:
subt = MyThread(f,args=(final_data_df,i))
threads_pool.append(subt)
for t in threads_pool:
t.setDaemon(True)
t.start()
iterative_df_list = []
for t in threads:
t.join()
iterative_df_list.append(t.get_result())
final_data_df = reduce(lambda a,b: pd.concat([a,b],sort=Fasle),iterative_df_list).reset_index(drop=True)