进程与线程

什么是进程(process)?

An executing instance of a program is called a process.

Each process provides the resources needed to execute a program. A process has a virtual address space, executable code, open handles to system objects, a security context, a unique process identifier, environment variables, a priority class, minimum and maximum working set sizes, and at least one thread of execution. Each process is started with a single thread, often called the primary thread, but can create additional threads from any of its threads.

  • 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程
  • 程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

有了进程为什么还要线程?

进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:

1
进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

2
3
进程在执行的过程中如果阻塞,

4
例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

例如,我们在使用qq聊天, qq做为一个独立进程如果同一时间只能干一件事,那他如何实现在同一时刻 既能监听键盘输入、又能监听其它人给你发的消息、同时还能把别人发的消息显示在屏幕上呢?你会说,操作系统不是有分时么?但我的亲,分时是指在不同进程间的分时呀, 即操作系统处理一会你的qq任务,又切换到word文档任务上了,每个cpu时间片分给你的qq程序时,你的qq还是只能同时干一件事呀。

再直白一点, 一个操作系统就像是一个工厂,工厂里面有很多个生产车间,不同的车间生产不同的产品,每个车间就相当于一个进程,且你的工厂又穷,供电不足,同一时间只能给一个车间供电,为了能让所有车间都能同时生产,你的工厂的电工只能给不同的车间分时供电,但是轮到你的qq车间时,发现只有一个干活的工人,结果生产效率极低,为了解决这个问题,应该怎么办呢?。。。。没错,你肯定想到了,就是多加几个工人,让几个人工人并行工作,这每个工人,就是线程!

什么是线程(thread)?

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

A thread is an execution context, which is all the information a CPU needs to execute a stream of instructions.

Suppose you're reading a book, and you want to take a break right now, but you want to be able to come back and resume reading from the exact point where you stopped. One way to achieve that is by jotting down the page number, line number, and word number. So your execution context for reading a book is these 3 numbers.

If you have a roommate, and she's using the same technique, she can take the book while you're not using it, and resume reading from where she stopped. Then you can take it back, and resume it from where you were.

Threads work in the same way. A CPU is giving you the illusion that it's doing multiple computations at the same time. It does that by spending a bit of time on each computation. It can do that because it has an execution context for each computation. Just like you can share a book with your friend, many tasks can share a CPU.

On a more technical level, an execution context (therefore a thread) consists of the values of the CPU's registers.

Last: threads are different from processes. A thread is a context of execution, while a process is a bunch of resources associated with a computation. A process can have one or many threads.

Clarification: the resources associated with a process include memory pages (all the threads in a process have the same view of the memory), file descriptors (e.g., open sockets), and security credentials (e.g., the ID of the user who started the process).

进程与线程的区别?

  1. Threads share the address space of the process that created it; processes have their own address space.
  2. Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.
  3. Threads can directly communicate with other threads of its process; processes must use interprocess communication to communicate with sibling processes.
  4. New threads are easily created; new processes require duplication of the parent process.
  5. Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.
  6. Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes.

Python GIL(Global Interpreter Lock)【全局解释器锁】  

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

上面的核心意思就是,无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行,擦。。。,那这还叫什么多线程呀?莫如此早的下结结论,听我现场讲。

首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL

这篇文章透彻的剖析了GIL对python多线程的影响,强烈推荐看一下:http://www.dabeaz.com/python/UnderstandingGIL.pdf

Python threading模块

线程有2种调用方式,如下:

直接调用

1
import threading

2
import time

3
4
5
def sayhi(num):  # 定义每个线程要运行的函数

6
7
    print("running on number:%s" % num)

8
9
    time.sleep(3)

10
11
12
if __name__ == '__main__':

13
    t1 = threading.Thread(target=sayhi, args=(1,))  # 生成一个线程实例

14
    t2 = threading.Thread(target=sayhi, args=(2,))  # 生成另一个线程实例

15
16
    t1.start()  # 启动线程

17
    t2.start()  # 启动另一个线程

18
19
    print(t1.getName())  # 获取线程名

20
    print(t2.getName())

21
22
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

23
# running on number:1

24
# running on number:2

25
# Thread-1

26
# Thread-2

继承式调用

1
import threading

2
import time

3
4
5
class MyThread(threading.Thread):

6
    def __init__(self, num):

7
        threading.Thread.__init__(self)

8
        self.num = num

9
10
    def run(self):  # 定义每个线程要运行的函数

11
12
        print("running on number:%s" % self.num)

13
14
        time.sleep(3)

15
16
17
if __name__ == '__main__':

18
    t1 = MyThread(1)

19
    t2 = MyThread(2)

20
    t1.start()

21
    t2.start()

22
23
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

24
# running on number:1

25
# running on number:2

Join & Daemon

Some threads do background tasks, like sending keepalive packets, or performing periodic garbage collection, or whatever. These are only useful when the main program is running, and it's okay to kill them off once the other, non-daemon, threads have exited.

Without daemon threads, you'd have to keep track of them, and tell them to exit, before your program can completely quit. By setting them as daemon threads, you can let them run and forget about them, and when your program quits, any daemon threads are killed automatically.

1
1 Python 默认参数创建线程后,不管主线程是否执行完毕,都会等待子线程执行完毕才一起退出,有无join结果一样

2
3
4
2 如果创建线程,并且设置了daemon为true,即thread.setDaemon(True), 则主线程执行完毕后自动退出,

5
不会等待子线程的执行结果。而且随着主线程退出,子线程也消亡。

6
7
8
3 join方法的作用是阻塞,等待子线程结束,join方法有一个参数是timeout,

9
即如果主线程等待timeout,子线程还没有结束,则主线程强制结束子线程。

10
11
12
4 如果线程daemon属性为False, 则join里的timeout参数无效。主线程会一直等待子线程结束。

13
14
15
5 如果线程daemon属性为True, 则join里的timeout参数是有效的, 主线程会等待timeout时间后,结束子线程。

16
此处有一个坑,即如果同时有N个子线程join(timeout),那么实际上主线程会等待的超时时间最长为 N * timeout,

17
 因为每个子线程的超时开始时刻是上一个子线程超时结束的时刻。

        Note:Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.) may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable signalling mechanism such as an Event.
Python多线程编程时,经常会用到join()和setDaemon()方法,今天特地研究了一下两者的区别。
    1、join ()方法:主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。
原型:join([timeout])
        里面的参数时可选的,代表线程运行的最大时间,即如果超过这个时间,不管这个此线程有没有执行完毕都会被回收,然后主线程或函数都会接着执行的。
例子:
1
import threading

2
import time

3
class MyThread(threading.Thread):

4
        def __init__(self,id):

5
                threading.Thread.__init__(self)

6
                self.id = id

7
        def run(self):

8
                x = 0

9
                time.sleep(10)

10
                print(self.id)

11
12
13
if __name__ == "__main__":

14
        t1=MyThread(999)

15
        t1.start()

16
        for i in range(5):

17
                print(i)

18
            

19
            

20
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

21
# 0

22
# 1

23
# 2

24
# 3

25
# 4

        机器上运行时,4和999之间,有明显的停顿。解释:线程t1 start后,主线程并没有等线程t1运行结束后再执行,而是先把5次循环打印执行完毕(打印到4),然后sleep(10)后,线程t1把传进去的999才打印出来。
        现在,我们把join()方法加进去(其他代码不变),看看有什么不一样,
例子:
1
import threading

2
import time

3
4
5
class MyThread(threading.Thread):

6
    def __init__(self, id):

7
        threading.Thread.__init__(self)

8
        self.id = id

9
10
    def run(self):

11
        x = 0

12
        time.sleep(10)

13
        print(self.id)

14
15
16
if __name__ == "__main__":

17
    t1 = MyThread(999)

18
    t1.start()

19
    t1.join()

20
    for i in range(5):

21
        print(i)

22
23
24
# D:\Python\python\python-3.6.1\Python36-64\python.exe

25
# 999

26
# 0

27
# 1

28
# 2

29
# 3

30
# 4

        机器上运行时,999之前,有明显的停顿。解释:线程t1 start后,主线程停在了join()方法处,等sleep(10)后,线程t1操作结束被join,接着,主线程继续循环打印。
2、setDaemon()方法。
        主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。
        例子:就是设置子线程随主线程的结束而结束:
1
import threading

2
import time

3
4
5
class MyThread(threading.Thread):

6
    def __init__(self, id):

7
        threading.Thread.__init__(self)

8
9
    def run(self):

10
        time.sleep(5)

11
        print("This is " + self.getName())

12
13
14
if __name__ == "__main__":

15
    t1 = MyThread(999)

16
    t1.setDaemon(True)

17
    t1.start()

18
    print("I am the father thread.")

19
20
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

21
# I am the father thread.

        可以看出,子线程t1中的内容并未打出。解释:t1.setDaemon(True)的操作,将父线程设置为了守护线程。根据setDaemon()方法的含义,父线程打印内容后便结束了,不管子线程是否执行完毕了。
         程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是,只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以用setDaemon方法了。
        所以,join和setDaemon的区别如上述的例子,它们基本是相反的。

线程锁(互斥锁Mutex)

一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况?

1
# -*- coding:UTF-8 -*-

2
import time

3
import threading

4
5
6
def addNum():

7
    global num  # 在每个线程中都获取这个全局变量

8
    print('--get num:', num)

9
    time.sleep(1)

10
    num -= 1  # 对此公共变量进行-1操作

11
12
13
num = 100  # 设定一个共享变量

14
thread_list = []

15
for i in range(100):

16
    t = threading.Thread(target=addNum)

17
    t.start()

18
    thread_list.append(t)

19
20
for t in thread_list:  # 等待所有线程执行完毕

21
    t.join()

22
23
print('final num:', num)

24
25
26
# D:\Python\python\python-3.6.1\Python36-64\python.exe

27
# --get num: 100

28
# --get num: 100

29
# --get num: 100

30
# --get num: 100

31
# ...

32
# --get num: 100

33
# --get num: 100

34
# final num: 0

35
36
# D:\Python\python\python-2.7.13\Python27\python2.exe

37
# ('--get num:', 100)

38
# ('--get num:', 100)

39
# ('--get num:', 100)

40
# ('--get num:', 100)

41
# ('--get num:', (100'--get num:'), 

42
# 100)

43
# 

44
# ('--get num:', 100)

45
# ('--get num:', 100)

46
# ('--get num:', 100)

47
# ('final num:', 1)

正常来讲,这个num结果应该是0, 但在python 2.7上多运行几次,会发现,最后打印出来的num结果不总是0,为什么每次运行的结果不一样呢? 哈,很简单,假设你有A,B两个线程,此时都 要对num 进行减1操作, 由于2个线程是并发同时运行的,所以2个线程很有可能同时拿走了num=100这个初始变量交给cpu去运算,当A线程去处完的结果是99,但此时B线程运算完的结果也是99,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是99。那怎么办呢? 很简单,每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。

 *注:不要在3.x上运行,不知为什么,3.x上的结果总是正确的,可能是自动加了锁

  • 加锁版本
1
# -*- coding:UTF-8 -*-

2
import time

3
import threading

4
5
6
def addNum():

7
    global num  # 在每个线程中都获取这个全局变量

8
    print('--get num:', num)

9
    time.sleep(1)

10
    lock.acquire()  # 修改数据前加锁

11
    num -= 1  # 对此公共变量进行-1操作

12
    lock.release()  # 修改后释放

13
14
15
num = 100  # 设定一个共享变量

16
thread_list = []

17
lock = threading.Lock()  # 生成全局锁

18
for i in range(100):

19
    t = threading.Thread(target=addNum)

20
    t.start()

21
    thread_list.append(t)

22
23
for t in thread_list:  # 等待所有线程执行完毕

24
    t.join()

25
26
print('final num:', num)

27
28
# D:\Python\python\python-2.7.13\Python27\python2.exe 

29
# ('--get num:', 100)

30
# ('--get num:', 100)

31
# ...

32
# ('--get num:', 100)

33
# ('--get num:', 100)

34
# ('final num:', 0)

GIL VS Lock

  • 机智的同学可能会问到这个问题,就是既然你之前说过了,Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要lock?
  • 注意啦,这里的lock是用户级的lock,跟那个GIL没关系 ,具体我们通过下图来看一下,就明白了。

那你又问了, 既然用户程序已经自己有锁了,那为什么C python还需要GIL呢?

加入GIL主要的原因是为了降低程序的开发的复杂度,比如现在的你写python不需要关心内存回收的问题,因为Python解释器帮你自动定期进行内存回收,你可以理解为python解释器里有一个独立的线程,每过一段时间它起wake up做一次全局轮询看看哪些内存数据是可以被清空的,此时你自己的程序 里的线程和 py解释器自己的线程是并发运行的,假设你的线程删除了一个变量,py解释器的垃圾回收线程在清空这个变量的过程中的clearing时刻,可能一个其它线程正好又重新给这个还没来及得清空的内存空间赋值了,结果就有可能新赋值的数据被删除了,为了解决类似的问题,python解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题,  这可以说是Python早期版本的遗留问题。

RLock(递归锁)

  • 说白了就是在一个大锁中还要再包含子锁
1
import threading, time

2
3
4
def run1():

5
    print("grab the first part data")

6
    lock.acquire()

7
    global num

8
    num += 1

9
    lock.release()

10
    return num

11
12
13
def run2():

14
    print("grab the second part data")

15
    lock.acquire()

16
    global num2

17
    num2 += 1

18
    lock.release()

19
    return num2

20
21
22
def run3():

23
    lock.acquire()

24
    res = run1()

25
    print('--------between run1 and run2-----')

26
    res2 = run2()

27
    lock.release()

28
    print(res, res2)

29
30
31
if __name__ == '__main__':

32
33
    num, num2 = 0, 0

34
    lock = threading.RLock()

35
    for i in range(10):

36
        t = threading.Thread(target=run3)

37
        t.start()

38
39
while threading.active_count() != 1:

40
    print(threading.active_count())

41
else:

42
    print('----all threads done---')

43
    print(num, num2)

44
    

45
# D:\Python\python\python-2.7.13\Python27\python2.exe 

46
# grab the first part data

47
# --------between run1 and run2-----

48
# grab the second part data

49
# (grab the first part data1

50
# , --------between run1 and run2-----1

51
# )grab the second part data

52
# 

53
# (2, 2)

54
# grab the first part data

55
# --------between run1 and run2-----

56
# grab the second part data

57
# (3, 3)

58
#  grab the first part data

59
# --------between run1 and run2-----

60
# grab the second part data

61
# (4, 4)

62
#  grab the first part data

63
# --------between run1 and run2-----

64
# grab the second part data

65
# (5, 5)

66
#  grab the first part data

67
# --------between run1 and run2-----

68
# grab the second part data

69
# (6, 6grab the first part data)

70
# 

71
# --------between run1 and run2-----

72
# grab the second part data

73
# (grab the first part data7

74
# , --------between run1 and run2-----7

75
# )grab the second part data

76
# 

77
# (8, 8)grab the first part data

78
# 

79
# --------between run1 and run2-----

80
# grab the second part data

81
# (9, 9)

82
# grab the first part data

83
# --------between run1 and run2-----

84
# grab the second part data

85
# (10, 10)

86
# 1

87
# ----all threads done---

88
# (10, 10)

89
# 

Semaphore(信号量)

  • 互斥锁 同时只允许一个线程更改数据而Semaphore是同时允许一定数量的线程更改数据 ,
  • 比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
1
import threading, time

2
3
4
def run(n):

5
    global  num

6
    semaphore.acquire()

7
    time.sleep(1)

8
    print("run the thread: %s\n" % n)

9
    num += n;

10
    semaphore.release()

11
12
13
if __name__ == '__main__':

14
15
    num = 0

16
    semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行

17
    for i in range(20):

18
        t = threading.Thread(target=run, args=(i,))

19
        t.start()

20
21
while threading.active_count() != 1:

22
    # print ("threading.active_count():",threading.active_count())

23
    pass

24
else:

25
    print('----all threads done---')

26
    print(num)

27
28
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

29
# run the thread: 2

30
# run the thread: 0

31
# run the thread: 4

32
# run the thread: 1

33
#

34
# run the thread: 3

35
#

36
#

37
#

38
#

39
# run the thread: 6

40
# run the thread: 5

41
#

42
# run the thread: 7

43
#

44
# run the thread: 9

45
# run the thread: 8

46
#

47
#

48
#

49
# run the thread: 14

50
# run the thread: 12

51
#

52
# run the thread: 13

53
# run the thread: 11

54
#

55
#

56
#

57
# run the thread: 10

58
#

59
# run the thread: 19

60
# run the thread: 16

61
# run the thread: 17

62
#

63
#

64
# run the thread: 15

65
#

66
#

67
# run the thread: 18

68
#

69
# ----all threads done---

70
# 190

71
#

Timer

  • This class represents an action that should be run only after a certain amount of time has passed
  • Timers are started, as with threads, by calling their start() method. The timer can be stopped (before its action has begun) by calling the cancel() method. The interval the timer will wait before executing its action may not be exactly the same as the interval specified by the user.
1
from threading import Timer

2
3
4
def hello():

5
    print("hello, world")

6
7
8
t = Timer(3.0, hello)

9
t.start()  

10
11
# after 3 seconds, "hello, world" will be printed

Events

  • An event is a simple synchronization object;
  • the event represents an internal flag, and threads can wait for the flag to be set, or set or clear the flag themselves.
1
event = threading.Event()
  • # a client thread can wait for the flag to be set
1
event.wait()
  • # a server thread can set or reset it
1
event.set()

2
event.clear()

  • If the flag is set, the wait method doesn’t do anything.
  • If the flag is cleared, wait will block until it becomes set again.
  • Any number of threads may wait for the same event.
  • python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法wait、clear、set
    • 事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
    • clear:将“Flag”设置为False
    • set:将“Flag”设置为True
  • 用 threading.Event 实现线程间通信
  • 使用threading.Event可以使一个线程等待其他线程的通知,我们把这个Event传递到线程对象中,
  • Event默认内置了一个标志,初始值为False。
  • 一旦该线程通过wait()方法进入等待状态,直到另一个线程调用该Event的set()方法将内置标志设置为True时,该Event会通知所有等待状态的线程恢复运行。

通过Event来实现两个或多个线程间的交互:

  • 下面是一个红绿灯的例子,即启动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
1
import random

2
import threading

3
import time

4
5
6
def light():

7
    if not event.isSet():

8
        event.set()  # wait就不阻塞 #绿灯状态

9
    count = 0

10
    while True:

11
        print("count:",count)

12
        if count < 10:

13
            print('\033[42;1m--green light on---\033[0m')

14
        elif count < 13:

15
            print('\033[43;1m--yellow light on---\033[0m')

16
        elif count < 20:

17
            if event.isSet():

18
                event.clear()

19
            print('\033[41;1m--red light on---\033[0m')

20
        else:

21
            count = 0

22
            event.set()  # 打开绿灯

23
        time.sleep(1)

24
        count += 1

25
26
27
def car(n):

28
    while 1:

29
        time.sleep(random.randrange(10))

30
        if event.isSet():  # 绿灯

31
            print("car [%s] is running.." % n)

32
        else:

33
            print("car [%s] is waiting for the red light.." % n)

34
35
36
if __name__ == '__main__':

37
    event = threading.Event()

38
    Light = threading.Thread(target=light)

39
    Light.start()

40
    for i in range(3):

41
        t = threading.Thread(target=car, args=(i,))

42
        t.start()

  • 这里还有一个event使用的例子,员工进公司门要刷卡, 我们这里设置一个线程是“门”,
  • 再设置几个线程为“员工”,员工看到门没打开,就刷卡,刷完卡,门开了,员工就可以通过。
1
#_*_coding:utf-8_*_

2
__author__ = 'Alex Li'

3
import threading

4
import time

5
import random

6
7
def door():

8
    door_open_time_counter = 0

9
    while True:

10
        if door_swiping_event.is_set():

11
            print("\033[32;1mdoor opening....\033[0m")

12
            door_open_time_counter +=1

13
14
        else:

15
            print("\033[31;1mdoor closed...., swipe to open.\033[0m")

16
            door_open_time_counter = 0 #清空计时器

17
            door_swiping_event.wait()

18
19
20
        if door_open_time_counter > 3:#门开了已经3s了,该关了

21
            door_swiping_event.clear()

22
23
        time.sleep(0.5)

24
25
26
def staff(n):

27
28
    print("staff [%s] is comming..." % n )

29
    while True:

30
        if door_swiping_event.is_set():

31
            print("\033[34;1mdoor is opened, passing.....\033[0m")

32
            break

33
        else:

34
            print("staff [%s] sees door got closed, swipping the card....." % n)

35
            print(door_swiping_event.set())

36
            door_swiping_event.set()

37
            print("after set ",door_swiping_event.set())

38
        time.sleep(0.5)

39
door_swiping_event  = threading.Event() #设置事件

40
41
42
door_thread = threading.Thread(target=door)

43
door_thread.start()

44
45
46
47
for i in range(5):

48
    p = threading.Thread(target=staff,args=(i,))

49
    time.sleep(random.randrange(3))

50
    p.start()

  

queue队列

Queue

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

基本FIFO队列

class Queue.Queue(maxsize=0)

FIFO即First in First Out,先进先出。Queue提供了一个基本的FIFO容器,使用方法很简单,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。如果maxsize小于或者等于0,队列大小没有限制

举个栗子:

1
import queue

2
3
q = queue.Queue()

4
5
for i in range(5):

6
    q.put(i)

7
8
while not q.empty():

9
    print(q.get())

10
11
# D:\Python\python\python-3.6.1\Python36-64\python.exe

12
# 0

13
# 1

14
# 2

15
# 3

16
# 4

17

LIFO队列

class Queue.LifoQueue(maxsize=0)

LIFO即Last in First Out,后进先出。与栈的类似,使用也很简单,maxsize用法同上

再举个栗子:

1
import queue

2
3
q = queue.LifoQueue()

4
5
for i in range(5):

6
    q.put(i)

7
8
while not q.empty():

9
    print(q.get())

10
11
12
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

13
# 4

14
# 3

15
# 2

16
# 1

17
# 0

可以看到仅仅是将Queue.Quenu类替换为Queue.LifiQueue类

优先级队列

class Queue.PriorityQueue(maxsize=0)

构造一个优先队列。maxsize用法同上。

1
import Queue

2
import threading

3
4
class Job(object):

5
    def __init__(self, priority, description):

6
        self.priority = priority

7
        self.description = description

8
        print 'Job:',description

9
        return

10
    def __cmp__(self, other):

11
        return cmp(self.priority, other.priority)

12
13
q = Queue.PriorityQueue()

14
15
q.put(Job(3, 'level 3 job'))

16
q.put(Job(10, 'level 10 job'))

17
q.put(Job(1, 'level 1 job'))

18
19
def process_job(q):

20
    while True:

21
        next_job = q.get()

22
        print 'for:', next_job.description

23
        q.task_done()

24
25
workers = [threading.Thread(target=process_job, args=(q,)),

26
        threading.Thread(target=process_job, args=(q,))

27
        ]

28
29
for w in workers:

30
    w.setDaemon(True)

31
    w.start()

32
33
q.join()

34
35
36
# D:\Python\python\python-2.7.13\Python27\python2.exe

37
# Job: level 3 job

38
# Job: level 10 job

39
# Job: level 1 job

40
# for: level 1 job

41
# for: level 3 job

42
# for: level 10 job

一些常用方法

  • task_done()
1
意味着之前入队的一个任务已经完成。由队列的消费者线程调用。

2
每一个get()调用得到一个任务,接下来的task_done()调用告诉队列该任务已经处理完毕。

3
4
如果当前一个join()正在阻塞,

5
它将在队列中的所有任务都处理完时恢复执行(即每一个由put()调用入队的任务都有一个对应的task_done()调用)。

  • join()
1
阻塞调用线程,直到队列中的所有任务被处理掉。

2
3
只要有数据被加入队列,未完成的任务数就会增加。

4
当消费者线程调用task_done()(意味着有消费者取得任务并完成任务),未完成的任务数就会减少。

5
当未完成的任务数降到0,join()解除阻塞。

  • put(item[, block[, timeout]])
1
将item放入队列中。

2
3
如果可选的参数block为True且timeout为空对象(默认的情况,阻塞调用,无超时)。

4
如果timeout是个正整数,阻塞调用进程最多timeout秒,如果一直无空空间可用,抛出Full异常(带超时的阻塞调用)。

5
如果block为False,如果有空闲空间可用将数据放入队列,否则立即抛出Full异常

6
其非阻塞版本为put_nowait等同于put(item, False)

  • get([block[, timeout]])
1
从队列中移除并返回一个数据。block跟timeout参数同put方法

2
3
其非阻塞方法为`get_nowait()`相当与get(False)

  • empty()
1
如果队列为空,返回True,反之返回False

生产者消费者模型

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

为什么要使用生产者和消费者模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

下面来学习一个最基本的生产者消费者模型的例子

1
import threading

2
import queue

3
4
5
def producer():

6
    for i in range(10):

7
        q.put("骨头 %s" % i)

8
9
    print("开始等待所有的骨头被取走...")

10
    q.join()

11
    print("所有的骨头被取完了...")

12
13
14
def consumer(n):

15
    while q.qsize() > 0:

16
        print("%s 取到" % n, q.get())

17
        q.task_done()  # 告知这个任务执行完了

18
19
20
q = queue.Queue()

21
22
p = threading.Thread(target=producer, )

23
p.start()

24
25
c1 = consumer("李闯")

26
27
28
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

29
# 开始等待所有的骨头被取走...

30
# 李闯 取到 骨头 0

31
# 李闯 取到 骨头 1

32
# 李闯 取到 骨头 2

33
# 李闯 取到 骨头 3

34
# 李闯 取到 骨头 4

35
# 李闯 取到 骨头 5

36
# 李闯 取到 骨头 6

37
# 李闯 取到 骨头 7

38
# 李闯 取到 骨头 8

39
# 李闯 取到 骨头 9

40
# 所有的骨头被取完了...

多进程multiprocessing

    multiprocessing is a package that supports spawning processes using an API similar to the threading module. The multiprocessing package offers both local and remote concurrency, effectively side-stepping(回避) the Global Interpreter Lock by using subprocesses instead of threads. Due to this, the multiprocessing module allows the programmer to fully leverage( 杠杆作用; 优势,力量) multiple processors on a given machine. It runs on both Unix and Windows.

1
from multiprocessing import Process

2
import time

3
4
5
def f(name):

6
    time.sleep(2)

7
    print('hello', name)

8
9
10
if __name__ == '__main__':

11
    p = Process(target=f, args=('bob',))

12
    p.start()

13
    p.join()

14
15
# D:\Python\python\python-3.6.1\Python36-64\python.exe

16
# hello bob

17

To show the individual process IDs involved, here is an expanded example:

1
from multiprocessing import Process

2
import os

3
4
5
def info(title):

6
    print(title)

7
    print('module name:', __name__)

8
    print('parent process:', os.getppid())

9
    print('process id:', os.getpid())

10
    print("\n\n")

11
12
13
def f(name):

14
    info('\033[31;1mfunction f\033[0m')

15
    print('hello', name)

16
17
18
if __name__ == '__main__':

19
    info('\033[32;1mmain process line\033[0m')

20
    p = Process(target=f, args=('bob',))

21
    p.start()

22
    p.join()

23
    

24
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

25
# main process line

26
# module name: __main__

27
# parent process: 7652

28
# process id: 5576

29
# 

30
# 

31
# 

32
# function f

33
# module name: __mp_main__

34
# parent process: 5576

35
# process id: 7888

36
# 

37
# 

38
# 

39
# hello bob

40
# 

41

进程间通讯  

  • 不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:

Queues

  • 使用方法跟threading里的queue差不多
1
from multiprocessing import Process, Queue

2
3
4
def f(q):

5
    q.put([42, None, 'hello'])

6
7
8
if __name__ == '__main__':

9
    q = Queue()

10
    p = Process(target=f, args=(q,))

11
    p.start()

12
    print(q.get())  

13
    p.join()

14
    

15
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

16
# [42, None, 'hello']

Pipes

  • The Pipe() function returns a pair of connection objects connected by a pipe which by default is duplex (two-way)(有两部分的). For example:
1
from multiprocessing import Process, Pipe

2
3
4
def f(conn):

5
    conn.send([42, None, 'hello'])

6
    conn.close()

7
8
9
if __name__ == '__main__':

10
    parent_conn, child_conn = Pipe()

11
    p = Process(target=f, args=(child_conn,))

12
    p.start()

13
    print(parent_conn.recv())

14
    p.join()

15
16
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

17
# [42, None, 'hello']

  • The two connection objects returned by Pipe() represent the two ends of the pipe.
  • Each connection object has send() and recv() methods (among others).
  • Note that data in a pipe may become corrupted(引起(计算机文件等的)错误; 破坏) if two processes (or threads) try to read from or write to the same end of the pipe at the same time.
  • Of course there is no risk of corruption from processes using different ends of the pipe at the same time.
Managers
  • A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.
  • A manager returned by Manager() will support types listdictNamespaceLockRLockSemaphoreBoundedSemaphoreConditionEventBarrierQueueValue and Array. For example,
1
from multiprocessing import Process, Manager

2
3
4
def f(d, l):

5
    d[1] = '1'

6
    d['2'] = 2

7
    d[0.25] = None

8
    l.append("A")

9
    print(l)

10
11
12
if __name__ == '__main__':

13
    with Manager() as manager:

14
        d = manager.dict()

15
16
        l = manager.list(range(5))

17
        p_list = []

18
        for i in range(10):

19
            p = Process(target=f, args=(d, l))

20
            p.start()

21
            p_list.append(p)

22
        for res in p_list:

23
            res.join()

24
25
        print(d)

26
        print(l)

27
28
# D:\Python\python\python-3.6.1\Python36-64\python.exe

29
# [0, 1, 2, 3, 4, 'A']

30
# [0, 1, 2, 3, 4, 'A', 'A']

31
# [0, 1, 2, 3, 4, 'A', 'A', 'A']

32
# [0, 1, 2, 3, 4, 'A', 'A', 'A', 'A']

33
# [0, 1, 2, 3, 4, 'A', 'A', 'A', 'A', 'A']

34
# [0, 1, 2, 3, 4, 'A', 'A', 'A', 'A', 'A', 'A']

35
# [0, 1, 2, 3, 4, 'A', 'A', 'A', 'A', 'A', 'A', 'A']

36
# [0, 1, 2, 3, 4, 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A']

37
# [0, 1, 2, 3, 4, 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A']

38
# [0, 1, 2, 3, 4, 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A']

39
# {1: '1', '2': 2, 0.25: None}

40
# [0, 1, 2, 3, 4, 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A']

进程同步

  • Without using the lock output from the different processes is liable(有…倾向的; 易…的) to get all mixed up.
1
from multiprocessing import Process, Lock

2
3
4
def f(l, i):

5
    l.acquire()

6
    try:

7
        print('hello world', i)

8
    finally:

9
        l.release()

10
11
12
if __name__ == '__main__':

13
    lock = Lock()

14
15
    for num in range(10):

16
        Process(target=f, args=(lock, num)).start()

17
18
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

19
# hello world 9

20
# hello world 7

21
# hello world 8

22
# hello world 6

23
# hello world 3

24
# hello world 1

25
# hello world 2

26
# hello world 5

27
# hello world 4

28
# hello world 0

进程池  

  • 进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
  • 进程池中有两个方法:
    • apply
    • apply_async
1
import multiprocessing

2
import os

3
import time

4
from datetime import datetime

5
6
7
def subprocess(number):

8
    # 子进程

9
    print('这是第%d个子进程' % number)

10
    pid = os.getpid()  # 得到当前进程号

11
    print('当前进程号:%s,开始时间:%s' % (pid, datetime.now().isoformat()))

12
    time.sleep(30)  # 当前进程休眠30秒

13
    print('当前进程号:%s,结束时间:%s' % (pid, datetime.now().isoformat()))

14
15
def Bar(arg):

16
    print('-->exec done:', arg)

17
18
def mainprocess():

19
    # 主进程

20
    print('这是主进程,进程编号:%d' % os.getpid())

21
    t_start = datetime.now()

22
    pool = multiprocessing.Pool()

23
    for i in range(8):

24
        pool.apply_async(subprocess, args=(i,), callback=Bar)

25
    pool.close()

26
    pool.join()

27
    t_end = datetime.now()

28
    print('主进程用时:%d毫秒' % (t_end - t_start).microseconds)

29
30
31
if __name__ == '__main__':

32
    # 主测试函数

33
    mainprocess()

34
    

35
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

36
# 这是主进程,进程编号:11224

37
# 这是第0个子进程

38
# 当前进程号:10640,开始时间:2017-08-10T08:34:36.821712

39
# 这是第1个子进程

40
# 当前进程号:10076,开始时间:2017-08-10T08:34:36.850713

41
# 这是第2个子进程

42
# 当前进程号:10996,开始时间:2017-08-10T08:34:36.859714

43
# 这是第3个子进程

44
# 当前进程号:10720,开始时间:2017-08-10T08:34:36.904716

45
# 当前进程号:10640,结束时间:2017-08-10T08:35:06.822428

46
# 这是第4个子进程

47
# 当前进程号:10640,开始时间:2017-08-10T08:35:06.822428

48
# -->exec done: None

49
# 当前进程号:10076,结束时间:2017-08-10T08:35:06.851429

50
# 这是第5个子进程

51
# 当前进程号:10076,开始时间:2017-08-10T08:35:06.851429

52
# -->exec done: None

53
# 当前进程号:10996,结束时间:2017-08-10T08:35:06.860430

54
# 这是第6个子进程

55
# 当前进程号:10996,开始时间:2017-08-10T08:35:06.860430

56
# -->exec done: None

57
# 当前进程号:10720,结束时间:2017-08-10T08:35:06.905432

58
# -->exec done: None

59
# 这是第7个子进程

60
# 当前进程号:10720,开始时间:2017-08-10T08:35:06.905432

61
# 当前进程号:10640,结束时间:2017-08-10T08:35:36.823144

62
# -->exec done: None

63
# 当前进程号:10076,结束时间:2017-08-10T08:35:36.852145

64
# -->exec done: None

65
# 当前进程号:10996,结束时间:2017-08-10T08:35:36.861146

66
# -->exec done: None

67
# 当前进程号:10720,结束时间:2017-08-10T08:35:36.906148

68
# -->exec done: None

69
# 主进程用时:417456毫秒

70
# 

71

  • 由于Python设计的限制[GIL](我说的是咱们常用的CPython)。最多只能用满1个CPU核心。
  • Python提供了非常好用的多进程包multiprocessing,你只需要定义一个函数,Python会替你完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换

1、新建单一进程

如果我们新建少量进程,可以如下:

1
import multiprocessing

2
import time

3
4
5
def func(msg):

6
    for i in range(3):

7
        print(msg)

8
        time.sleep(1)

9
10
11
if __name__ == "__main__":

12
    p = multiprocessing.Process(target=func, args=("hello",))

13
    p.start()

14
    p.join()

15
    print("Sub-process done.")

16
17
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

18
# hello

19
# hello

20
# hello

21
# Sub-process done.

2、使用进程池

是的,你没有看错,不是线程池。它可以让你跑满多核CPU,而且使用方法非常简单。

  • 注意要用apply_async,如果使用async,就变成阻塞版本了。
  • processes=4是最多并发进程数量。
1
import multiprocessing

2
import time

3
4
5
def func(msg):

6
    for i in range(3):

7
        print(msg)

8
        time.sleep(1)

9
        print("++++++++++")

10
11
12
if __name__ == "__main__":

13
    pool = multiprocessing.Pool(processes=4)

14
    for i in range(10):

15
        msg = "hello %d" % (i)

16
        pool.apply_async(func, (msg,))

17
    pool.close()

18
    pool.join()

19
    print("Sub-process(es) done.")

20
21
22
# D:\Python\python\python-3.6.1\Python36-64\python.exe

23
# hello 0

24
# hello 1

25
# hello 2

26
# hello 3

27
# ++++++++++

28
# hello 0

29
# ++++++++++

30
# hello 1

31
# ++++++++++

32
# hello 2

33
# ++++++++++

34
# hello 3

35
# ++++++++++

36
# hello 0

37
# ++++++++++

38
# hello 1

39
# ++++++++++

40
# hello 2

41
# ++++++++++

42
# hello 3

43
# ++++++++++

44
# hello 4

45
# ++++++++++

46
# hello 5

47
# ++++++++++

48
# hello 6

49
# ++++++++++

50
# hello 7

51
# ++++++++++

52
# hello 4

53
# ++++++++++

54
# hello 5

55
# ++++++++++

56
# hello 6

57
# ++++++++++

58
# hello 7

59
# ++++++++++

60
# hello 4

61
# ++++++++++

62
# hello 5

63
# ++++++++++

64
# hello 6

65
# ++++++++++

66
# hello 7

67
# ++++++++++

68
# hello 8

69
# ++++++++++

70
# hello 9

71
# ++++++++++

72
# ++++++++++

73
# ++++++++++

74
# hello 8

75
# ++++++++++

76
# hello 9

77
# ++++++++++

78
# hello 8

79
# ++++++++++

80
# hello 9

81
# ++++++++++

82
# ++++++++++

83
# Sub-process(es) done.

84
#

85
# Process finished with exit code 0

86

3、使用Pool,并需要关注结果

更多的时候,我们不仅需要多进程执行,还需要关注每个进程的执行结果,如下:

1
import multiprocessing

2
import time

3
4
5
def func(msg):

6
    for i in range(3):

7
        print(msg)

8
        time.sleep(1)

9
    print("++++++++")

10
    return "done " + msg

11
12
13
if __name__ == "__main__":

14
    pool = multiprocessing.Pool(processes=4)

15
    result = []

16
    for i in range(10):

17
        msg = "hello %d" % (i)

18
        result.append(pool.apply_async(func, (msg,)))

19
    pool.close()

20
    pool.join()

21
    for res in result:

22
        print(res.get())

23
    print("Sub-process(es) done.")

24
25
# D:\Python\python\python-3.6.1\Python36-64\python.exe 

26
# hello 0

27
# hello 1

28
# hello 2

29
# hello 3

30
# hello 0

31
# hello 1

32
# hello 2

33
# hello 3

34
# hello 0

35
# hello 1

36
# hello 2

37
# hello 3

38
# ++++++++

39
# hello 4

40
# ++++++++

41
# hello 5

42
# ++++++++

43
# hello 6

44
# ++++++++

45
# hello 7

46
# hello 4

47
# hello 5

48
# hello 6

49
# hello 7

50
# hello 4

51
# hello 5

52
# hello 6

53
# hello 7

54
# ++++++++

55
# hello 8

56
# ++++++++

57
# hello 9

58
# ++++++++

59
# ++++++++

60
# hello 8

61
# hello 9

62
# hello 8

63
# hello 9

64
# ++++++++

65
# ++++++++

66
# done hello 0

67
# done hello 1

68
# done hello 2

69
# done hello 3

70
# done hello 4

71
# done hello 5

72
# done hello 6

73
# done hello 7

74
# done hello 8

75
# done hello 9

76
# Sub-process(es) done.

参考来源: http://blog.csdn.net/zhangzheng0413/article/details/41728869/

参考来源: http://www.cnblogs.com/alex3714/articles/5230609.html

Python基础:进程、线程、协程(2)相关推荐

  1. Python之进程+线程+协程(异步、selectors模块、阻塞、非阻塞IO)

    文章目录 一.IO多路复用 二.selectors模块 本篇文字是关于IO多路复用的更深入一步的总结,上一篇 Python之进程+线程+协程(事件驱动模型.IO多路复用.select与epoll)对I ...

  2. Python之进程+线程+协程(并发与并行、GIL锁、同步锁、死锁、递归锁)

    文章目录 一.并发与并行 二.同步与异步 三.线程锁 1.GIL全局解释器锁 2.同步锁 3.死锁 4.递归锁 在Python中GIL解释器锁.同步锁.死锁.递归锁都是什么?怎么这么多锁,它们都是用来 ...

  3. Python之进程+线程+协程(事件驱动模型、IO多路复用、select与epoll)

    文章目录 一.事件驱动模型 二.IO多路复用 本篇文章是关于涉及网络编程与协程.进程之间结合的内容,其中事件驱动模型.IO多路复用.select与epoll的使用等方面的知识 一.事件驱动模型 1.事 ...

  4. Python之进程+线程+协程(进程的本质 与 threading线程模块)

    文章目录 基本概念 threading线程模块 本篇开始分析Python中的并发程序,也就是进程.线程.协程序的使用.由于是用自己的语言总结的,因此比较大白话,或者叫通俗易懂.而且很多细节方面没有具体 ...

  5. Python之进程+线程+协程(multiprocessing多进程模块)

    前几篇的多线程模块的各种规则和用法,本篇则是关于多进程模块的内容 1.multiprocessing的介绍 在Python中,由于有GIL解释器锁的存在,多线程就根本不是本质意义上的多线程,而是一个主 ...

  6. Python之进程+线程+协程(同步对象、信号量、队列)

    文章目录 Event同步对象 semaphore信号量 队列 本篇是关于Python进程方面的内容了,主要是Event同步对象,信号量和队列 Event同步对象 1.概念: 我们可以对一个线程set一 ...

  7. Python之进程+线程+协程(进程间通信、进程同步、进程池、回调函数)

    文章目录 进程间通信 进程同步 进程池 回调函数 本篇文章依然是进程.线程方面的内容,主要讲进程间的通信.进程队列.进程同步.进程池.进程同步和回调函数 进程间通信 进程就是两个独立的内存空间在运行, ...

  8. Python之进程+线程+协程(生产者消费者模型)

    本篇主要总结一下非常有名的生成者消费者模型 概念引用 1.为什么要使用生产者和消费者模型? 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程开发当中,如果生产者处理速度很快, ...

  9. linux的进程/线程/协程系列5:协程的发展复兴与实现现状

    协程的发展复兴与实现现状 前言 本篇摘要: 1. 协同制的发展史 1.1 协同工作制的提出 1.2 自顶向下,无需协同 1.3 协同式思想的应用 2. 协程的复兴 2.1 高并发带来的问题 2.2 制 ...

  10. Linux的进程/线程/协程系列4:进程知识深入总结:上篇

    Linux的进程/线程/协程系列4:进程/线程相关知识总结 前言 本篇摘要: 1. 进程基础知识 1.1 串行/并行与并发 1.2 临界资源与共享资源 1.3 同步/异步与互斥 1.4 进程控制原语 ...

最新文章

  1. remote: 此仓库的限制大小为: 2048 MB, 您的使用已经超出限额
  2. Oracle 技术支持之现场优化的思维路径
  3. 避免 UNIX 和 Linux 中的常见错误
  4. NDK开发环境安装,CDT安装,Cygwin安装
  5. 漫步微积分三十六——曲面的面积
  6. js未知数加已知数的结果数,三者的每个数字不重复
  7. python 复制列表内容_Python复制列表列表
  8. python中syntaxerror_【已解决】Python中两个星号**参数去传递给函数出错:SyntaxError invalid syntax...
  9. 计算机网络的各层及其协议,计算机网络的体系结构 (architecture) 是计算机网络的各层及其协议的集合...
  10. 「leetcode」763. 划分字母区间:【记录最远位置与双指针】详解
  11. 如何将Python程序打包成linux可执行文件
  12. ecshop后台首页mysql_ECSHOP后台自带数据库管理
  13. vue.js--v-show隐藏图片
  14. 关于“未使用GUID分区表”无法安装的解决方案
  15. c语言 error c2001,Visual Studio error C2001:常量中有换行符(解决办法)
  16. jarvisoj_level1
  17. 牛客网暑期ACM多校训练营(第四场)C(Chiaki Sequence Reloaded)
  18. DCC_2012_Sihong Su . Xiaohu Tang rotation symmetric boolean functions
  19. golang可以做什么
  20. 51单片机摇摇棒改字原理详解

热门文章

  1. shell学习-基础篇
  2. 用Winrar打造永不被杀的免杀捆绑器!
  3. python 可以用excel做词云图嘛_今天从零教你开始利用Python打造词云图!
  4. iOS storyboard 用法详解
  5. 一款牛逼的Java工具类库,GitHub星标11.6k+
  6. 如何用django来创建一个网站
  7. 脱 PECompact 2.x - Jeremy Collake 壳
  8. 中序表达式转其他顺序 - 表达式加括号法
  9. C++判断一个数是不是质数
  10. C语言sfr指令,51单片机的指令和sfr汇总.pdf