python 进程 VS 线程

                                        进程 VS 线程

一、进程和线程的区别

    1、进程是对运行时程序的封装,是系统资源调度和分配的基本单位。

    2、线程是进程的子任务,CPU 调度和分配的基本单位,实现进程内并发。

    3、一个进程可以包含多个线程,线程依赖进程存在,并共享进程内存。

 

二、线程安全

    1、python 哪些操作是线程安全的?

        (1) 一个操作可以在多线程环境中安全使用,获取正确的结果。

        (2) 线程安全的操作好比线程是顺序执行而不是并发执行的(i += 1)。

        (3) 一般如果涉及到写操作需要考虑如何让多个线程安全访问数据。

 

三、线程同步的方式

    了解线程同步的方式,如何保证线程安全。

    1、互斥锁(Lock):通过互斥机制防止多个线程同时访问公共资源。

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

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

        为了防止上面情况的发生,就出现了互斥锁(Lock)。

from threading import Thread, Lock
import time


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

    def run(self):
        global num
        print("开启线程:" + self.name)
        # 获得锁,用于线程同步
        threadLock.acquire()
        num += 1
        print_time(self.name, self.counter, 3)
        print("num: ", num)
        # 释放锁,开启下一个线程
        threadLock.release()
        print("退出线程:" + self.name)


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


if __name__ == "__main__":
    threadLock = Lock()
    thread_list = []
    num = 0

    # 创建线程
    # thread1 = MyThread(1, "Thread-1", 1)
    # thread2 = MyThread(2, "thread-2", 2)

    # 开启新线程
    # thread1.start()
    # thread2.start()

    # 添加线程到线程列表
    # thread_list.append(thread1)
    # thread_list.append(thread2)

    for i in range(10):
        thread = MyThread(i, "Thread-" + str(i), i)
        thread.start()
        thread_list.append(thread)

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

    2、递归锁(RLock):

        RLock 类的用法和 Lock 类一模一样,但它支持嵌套,在多个锁没有释放的时候一般会使用RLock类。

        (1) 加锁前: 

from threading import Thread, RLock
import time

def Func(lock):
    global num
    # lock.acquire()
    num += 1
    time.sleep(1)
    print(num)
    # lock.release()


if __name__ == "__main__":
    num = 0
    lock = RLock()
    for i in range(10):
        thread = Thread(target=Func, args=(lock,))
        thread.start()
        # thread.join()

# 运行结果:
10
10
10
10
10
10
10
10
10
10

        (2) 加锁后:          

from threading import Thread, RLock
import time

def Func(lock):
    global num
    # lock.acquire()
    num += 1
    time.sleep(1)
    print(num)
    # lock.release()


if __name__ == "__main__":
    num = 0
    lock = RLock()
    for i in range(10):
        thread = Thread(target=Func, args=(lock,))
        thread.start()
        # thread.join()

# 运行结果:
1
2
3
4
5
6
7
8
9
10

    3、信号量(Semphare):控制同一时刻多个线程访问同一资源的线程数。

        互斥锁同时只允许一个线程更改数据,而 Semaphore 是同时允许一定数量的线程去更改数据,比如一个餐桌有三个座位,那最多只允许三个人在餐桌座位上就餐,后面的人只能等前面的人就餐结束才能在餐桌座位就餐。

        (1) 未加锁:        

from threading import Thread, BoundedSemaphore, active_count
import time


def run(n, semaphore):
    global num
    # semaphore.acquire()
    num += 1
    time.sleep(1)
    print("run the thread: %s\n" % n)
    print("num: ", num)
    # semaphore.release()


if __name__ == "__main__":
    num = 0
    semaphore = BoundedSemaphore(3)     # 最多允许3个线程同时运行
    for i in range(10):
        thread = Thread(target=run, args=("thread-%s" %i, semaphore))
        thread.start()

    while active_count() != 1:    # 返回当前处于激活状态的Thread对象个数,返回的个数等于threading.enumerate()返回的list的长度
        pass
    else:
        print("-------- all threads done --------")

# 运行结果:
run the thread: thread-7
run the thread: thread-6

num:  10
run the thread: thread-8

num:  10
run the thread: thread-3

num:  10
run the thread: thread-4

num:  10
run the thread: thread-0
run the thread: thread-9

num:  10
run the thread: thread-1

num:  10
run the thread: thread-2

run the thread: thread-5

num:  10
num:  10

num:  10

num:  10
-------- all threads done --------

        (2) 加锁后:

from threading import Thread, BoundedSemaphore, active_count
import time

def run(n, semaphore):
    global num
    semaphore.acquire()
    num += 1
    time.sleep(1)
    print("run the thread: %s\n" % n)
    print("num: ", num)
    semaphore.release()


if __name__ == "__main__":
    num = 0
    semaphore = BoundedSemaphore(3)     # 最多允许3个线程同时运行
    for i in range(10):
        thread = Thread(target=run, args=("thread-%s" %i, semaphore))
        thread.start()
        # thread.join()

    while active_count() != 1:    # 返回当前处于激活状态的Thread对象个数,返回的个数等于threading.enumerate()返回的list的长度
        pass
    else:
        print("-------- all threads done --------")

# 运行结果:
run the thread: thread-0

num:  3
run the thread: thread-2

num:  4
run the thread: thread-1

num:  4
run the thread: thread-5
run the thread: thread-3

num:  6

run the thread: thread-4

num:  7
num:  7
run the thread: thread-6
run the thread: thread-7

num:  9
run the thread: thread-8

num:  9

num:  10
run the thread: thread-9

num:  10
-------- all threads done --------

    4、事件(Event):通过通知的方式保持多个线程同步。

        事件处理的机制:全局定义了一个 flag,当 flag 值为 False,那么 event.wait() 就会阻塞,当 flag 值为 True,那么 event.wait() 便不再阻塞。

        python 线程的事件用于主线程控制其他线程的执行,事件是一个简单的线程同步对象,其主要提供以下几个方法:

            (1) 创建 event 对象。      

from threading import Event

event = Event()

            (2) event.isSet() 方法,返回event状态值 True 和 False。              

from threading import Event

event = Event()
print(event.is_set())
print(event.isSet())

# 运行结果:
False
False

            (3) event.wait() 方法,等待的意思,如果你在某一个线程里面调用了 wait,它会判断标志位是 True 还是 False,如果是 True,wait 什么都不做继续往下执行,如果是 False会阻塞,等到标志位被改成 True 才能执行。      

from threading import Event

event = Event()
print(event.is_set())
print(event.isSet())


def childtd():
    print("wait...")
    event.wait()    # 如果标志位为 False阻塞,True 执行
    print("connetct server")


thread = Thread(target=childtd, args=())
thread.start()

# 运行结果:
False
False
wait...

            (4) event.wait(3):等待3秒钟如果标志位还没恢复过来,那就不等了会继续往下执行。它怎么用呢,我们不能在程序3秒后继续往下执行,那就失去了本身的意义,我们可以在等待过程中每2秒打印信息。   

from threading import Event

event = Event()


def childtd():
    while not event.isSet():
        print("等待中...")
        event.wait(3)  # 如果标志位为 False阻塞,True 执行
    print("wait...")
    print("connetct server")


thread = Thread(target=childtd, args=())
thread.start()

# 运行结果:
等待中...
等待中...
等待中...
等待中...
等待中...
.........

            (5) event.set() 方法,默认标志位是 False,用 set 方法可以设置标志位的值为 True。在主线程修改标志位为 True,让子线程继续往下执行。              

from threading import Event

event = Event()
print(event.is_set())
print(event.isSet())


def childtd():
    print("wait...")
    event.wait()    # 如果标志位为 False阻塞,True 执行
    print("connetct server")


thread = Thread(target=childtd, args=())
thread.start()
event.set()    # 设置标志位值为 True

# 运行结果:
False
False
wait...
connetct server

            (6) event.clear() 方法,恢复 event 的状态值为 False。       

from threading import Event

event = Event()
print(event.is_set())
event.set()
print(event.is_set())
event.clear()
print(event.isSet())

# 运行结果:
False
True
False

        

四、进程间通信的方式

    Inter-Process Communication 进程间传递信号或者数据。

    1、管道/匿名管道/有名管道(pipe)。

    2、信号(Signal):比如用户使用 Ctrl + c 产生 SIGINT 程序终止信号。

    3、消息队列(Message)。

    4、共享内存(share memory)。

    5、信号量(Semaphore)。

    6、套接字(socket):最常用的方式,我们的 web 应用都是这种方式。

 

五、python 中如何使用多线程

    1、threading 模块,普通创建模式。

        (1) threading.Thread 类用来创建线程。

        (2) start() 方法启动线程。

        (3) 可以用 join() 等待线程结束。      

import threading
import time


def Func(n):
    print("task ", n)
    time.sleep(1)
    print("2s")
    time.sleep(1)
    print("1s")
    time.sleep(1)
    print("0s")
    time.sleep(1)


if __name__ == "__main__":
    t1 = threading.Thread(target=Func, args=("t1",))
    t2 = threading.Thread(target=Func, args=("t2", ))
    t1.start()
    t2.start()

# 运行结果:
task  t1
task  t2
2s
2s
1s
1s
0s
0s

    2、自定义线程,继承 threading.Thread 来自定义线程类,其本质是重构 Thread 类中的 run 方法。

from threading import Thread
import time

exitFlag = 0


class myThread(Thread):
    def __init__(self, threadID, name, counter):
        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 \n" %(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("退出主线程")

# 运行结果:
开始线程:Thread-1
开始线程:Thread-2
Thread-1: Wed Jun  3 15:20:33 2020 

Thread-2: Wed Jun  3 15:20:34 2020 

Thread-1: Wed Jun  3 15:20:34 2020 

Thread-1: Wed Jun  3 15:20:35 2020 

Thread-2: Wed Jun  3 15:20:36 2020 

Thread-1: Wed Jun  3 15:20:36 2020 

Thread-1: Wed Jun  3 15:20:37 2020 

退出线程:Thread-1
Thread-2: Wed Jun  3 15:20:38 2020 

Thread-2: Wed Jun  3 15:20:40 2020 

Thread-2: Wed Jun  3 15:20:42 2020 

退出线程:Thread-2
退出主线程

    3、应用场景:一般用在 IO 密集型程序中。

 

六、python 中如何使用多进程

    python 有 GIL,可以用多进程实现 CPU 密集程序。

    1、multiprocessing 多进程模块。

    2、multiprocessing.Process 类实现多进程。

from multiprocessing import Process
import os


def info(title):
    print(title)
    print('module name:', __name__)
    print('parent process:', os.getppid())
    print('process id:', os.getpid())


def f(name):
    info('function f')
    print('hello', name)


if __name__ == '__main__':
    info('main line')
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

# 运行结果:
main line
module name: __main__
parent process: 11508
process id: 12408
function f
module name: __mp_main__
parent process: 12408
process id: 14316
hello bob

    3、一般用在 CPU 密集程序中,避免 GIL 的影响。  

    4、进程池

from multiprocessing import Pool
import time


def fun(x):
    return x * x


if __name__ == "__main__":
    with Pool(processes=4) as pool:    # start 4 worker processes 启动4个辅助进程
        # evaluate "f(10)" asynchronously in a single process
        # 在单个进程中异步计算 fun(10)
        result = pool.apply_async(fun, (10,))
        # prints "100" unless your computer is *very* slow
        # 打印 100,除非你的电脑非常慢
        print(result.get(timeout=1))

        # prints "[0, 1, 4,..., 81]"
        print(pool.map(fun, range(10)))

        it = pool.imap(fun, range(10))
        # # prints "0"
        print(next(it))
        # # prints "1"
        print(next(it))
        # prints "4" unless your computer is *very* slow
        # 打印 4,除非你的电脑非常慢
        print(it.next(timeout=1))

        result = pool.apply_async(time.sleep, (10,))
        # raises multiprocessing.TimeoutError
        # get()用于获取执行结果。
        # 如果 timeout 不是 None 并且在 timeout 秒内仍然没有执行完得到结果,则抛出 multiprocessing.TimeoutError 异常。
        # 如果远程调用发生异常,这个异常会通过 get() 重新抛出。
        print(result.get(timeout=1))

        pool.close()  # 允许pool中的进程关闭(close必须在join前面,可以理解close相当于一个开关吧)
        pool.join()  # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值