# 多线程-多进程

## 多线程-多进程-锁

哪些情况`适合用多线程`呢：

​ 只要在进行耗时的IO操作的时候，能释放GIL，所以只要在IO密集型的代码里，用多线程就很合适

哪些情况`适合用多进程`呢：

​ 用于计算密集型，比如计算某一个文件夹的大小。

包含：

> ThreadPool、DummyPool 线程池 ProcessPool 进程池 threading 线程

进程和线程的关系：

> (1)一个线程只能属于一个进程，而一个进程可以有多个线程，但至少有一个线程。
>
> (2)资源分配给进程，同一进程的所有线程共享该进程的所有资源。
>
> (3)CPU分给线程，即真正在CPU上运行的是线程。

`并行处理（Parallel Processing）`：是计算机系统中能`同时执行两个或更多个处理`的一种计算方法 `并发处理(concurrency Processing)`：指一个时间段中有几个程序都处于已启动运行到运行完毕之间，且这几个程序都是在同一个处理机(CPU)上运行，但`任一个时刻点上只有一个程序在处理机(CPU)`上运行 并发的关键是你有处理多个任务的能力，不一定要同时。并行的关键是你有同时处理多个任务的能力。所以说，并行是并发的子集

![img](https://images2015.cnblogs.com/blog/877318/201705/877318-20170503122648148-1238009778.png)

ThreadPool()和Pool()，默认启动的进程/线程数都为CPU数，如果python获取不到CPU数则默认为1

ThreadPool线程池：

```python
from multiprocessing.pool import ThreadPool

def run(i):
    i = i * i
    # return i # return和不return对进程池运行速度会有比较大影响，不return效率更高

def thread_pool(num):
    p = ThreadPool(num)
    start_time = time.time()
    ret = p.map(run, range(max_range))
    p.close()
    p.join()
    print("thread_pool  %d, costTime: %fs ret.size: %d" % (num, (time.time() - start_time), len(ret)))


if __name__ == "__main__":
    for i in range(1, 9):
        thread_pool(i)
```

DummyPool线程池：

```python
from multiprocessing.dummy import Pool as DummyPool

def run(i):
    i = i * i
    # return i # return和不return对进程池运行速度会有比较大影响，不return效率更高

def dummy_pool(num):
    p = DummyPool(num)
    start_time = time.time()
    ret = p.map(run, range(max_range))
    p.close()
    p.join()
    print("dummy_pool   %d, costTime: %fs ret.size: %d" % (num, (time.time() - start_time), len(ret)))


if __name__ == "__main__":
    for i in range(1, 9):
        dummy_pool(i)
```

Pool进程池：

```python
from multiprocessing import Pool as ProcessPool

def process_pool(num):
    p = ProcessPool(num)
    start_time = time.time()
    ret = p.map(run, range(max_range))
    p.close()
    p.join()
    print("process_pool %d, costTime: %fs ret.size: %d" % (num, (time.time() - start_time), len(ret)))

if __name__ == "__main__":
    for i in range(1, 9):
                process_pool(i)
```

### python GIL全局解释器锁

为什么会有gil？：

```
   随着电脑多核cpu的出现核cpu频率的提升，为了充分利用多核处理器，进行多线程的编程方式更为普及，随之而来的困难是线程之间数据的一致性和状态同步，而python也利用了多核，所以也逃不开这个困难，为了解决这个数据不能同步的问题，设计了gil全局解释器锁。
```

这种情况，会出现问题，共同竞争global\_num：

```python
import threading
global_num = 0


def test1():
    global global_num
    for i in range(1000000):
        global_num += 1

    print("test1", global_num)


def test2():
    global global_num
    for i in range(1000000):
        global_num += 1

    print("test2", global_num)

t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
```

这回能得到我们想要的结果：

```python
import threading

global_num = 0
lock = threading.Lock()


def test1():
    global global_num
    lock.acquire()
    for i in range(1000000):
        global_num += 1
    lock.release()
    print("test1", global_num)


def test2():
    global global_num
    lock.acquire()
    for i in range(1000000):
        global_num += 1
    lock.release()
    print("test2", global_num)


t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)

t1.start()
t2.start()
```

## threadpool  线程池

pip install threadpool

```python
pool = ThreadPool(poolsize)  
requests = makeRequests(some_callable, list_of_args, callback)  
[pool.putRequest(req) for req in requests]  
pool.wait()
```

第一行定义了一个线程池，表示最多可以创建poolsize这么多线程；

第二行是调用makeRequests创建了要开启多线程的函数，以及函数相关参数和回调函数，其中回调函数可以不写，default是无，也就是说makeRequests只需要2个参数就可以运行；

第三行用法比较奇怪，是将所有要运行多线程的请求扔进线程池，\[pool.putRequest(req) for req in requests]等同于

for req in requests:

pool.putRequest(req)

第四行是等待所有的线程完成工作后退出。

**多参数：**

```python
import threadpool

def hello(m, n, o):
    print("m = %s, n = %s, o = %s" % (m, n, o))


if __name__ == '__main__':

    # 方法1
    lst_vars_1 = ['1', '2', '3']
    lst_vars_2 = ['4', '5', '6']
    func_var1 = [(lst_vars_1, None), (lst_vars_2, None)]
    # 方法2
    dict_vars_1 = {'m': '1', 'n': '2', 'o': '3'}
    dict_vars_2 = {'m': '4', 'n': '5', 'o': '6'}
    func_var2 = [(None, dict_vars_1), (None, dict_vars_2)]

    pool = threadpool.ThreadPool(3)
    # 参数必须是包含2个元素的元组，第一个解析list，第二个解析dict
    # (*request.args, **request.kwds)
    requests = threadpool.makeRequests(hello, func_var2)
    [pool.putRequest(req) for req in requests]
    pool.wait()

#output
m = 1, n = 2, o = 3
m = 4, n = 5, o = 6
```

## threading

```python
import threading
import time

def countNum(n): # 定义某个线程要运行的函数
    print("running on number:%s" %n)
    time.sleep(3)

if __name__ == '__main__':
    t1 = threading.Thread(target=countNum,args=(23,)) #生成一个线程实例
    t2 = threading.Thread(target=countNum,args=(34,))
    t1.start() #启动线程
    t2.start()
    print("ending!")
```

或者继承：

```python
import threading
import time

class MyThread(threading.Thread):

    def __init__(self,num):
        threading.Thread.__init__(self)
        self.num=num

    def run(self):
        print("running on number:%s" %self.num)
        time.sleep(3)

t1=MyThread(56)
t2=MyThread(78)

t1.start()
t2.start()

t1.join()    # 在子线程t1完成运行之前，这个子线程的父线程将一直被阻塞。
t2.join()    # 在子线程t2完成运行之前，这个子线程的父线程将一直被阻塞。
#join([time]) 可选超时时间

print("ending")
```

![img](https://images2015.cnblogs.com/blog/877318/201705/877318-20170504143051961-777706802.png)

* threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前，不包括启动前和终止后的线程。
* threading.activeCount(): 返回正在运行的线程数量，与len(threading.enumerate())有相同的结果。
* **isAlive():** 返回线程是否活动的。
* **getName():** 返回线程名。
* **setName():** 设置线程名。

**同步**

Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步，这两个对象都有 acquire 方法和 release 方法，对于那些需要每次只允许一个线程操作的数据，可以将其操作放到 acquire 和 release 方法之间.

```python
import threading


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()


threadLock = threading.Lock()
threads = []
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

threads.append(thread1)
threads.append(thread2)

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://im-qianuxn.gitbook.io/pytorch/ji-suan-ji/python/duo-xian-cheng-jing-cheng.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
