Python 多线程的使用

        多线程,python中创建多线程模块是Threading模块,Python中Thread是比较底层的模块,Threading是对Thread模块的封装。

         首先使用threading模块来创建线程;

import threading


def say():
    print("-------子线程-------")

if __name__ == "__main__":
    for i in range(5):
        thread = threading.Thread(target=say)  # 创建多线程实例,Thread中target是需要子线程执行的函数
        thread.start()  # 启动多线程

      第二种方式,通过集成的方式来创建线程;

import threading
import time


class test_thread(threading.Thread): # 通过继承threading.Thread重写run方法的形式,创建子线程
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm	" + self.name
        print(msg)


def test():
    for i in range(5):
        t = test_thread() # 实例化线程对象
        t.start()


if __name__ == "__main__":
    test()

运行结果:

I'm	Thread-5
I'm	Thread-3
I'm	Thread-4
I'm	Thread-2
I'm	Thread-1

共享全局变量

    线程还有一个特点是,全局变量是共享的,不同于进程全局变量之间是不同享,想要使用被改变的变量,需要采取一定的措施(进程之间的通信),才能共享数据。

线程与进程的区别:

定义的不同

1.进程是系统进⾏资源分配和调度的⼀个独⽴单位.

2.线程是进程的⼀个实体,是CPU调度和分派的基本单位,它是⽐进程更⼩的 能独⽴运⾏的基本单位.线程⾃⼰基本上不拥有系统资源,只拥有⼀点在运 ⾏中必不可少的资源(如程序计数器,⼀组寄存器和栈),但是它可与同属⼀个进程的其他的线程共享进程所拥有的全部资源.

区别

⼀个程序⾄少有⼀个进程,⼀个进程⾄少有⼀个线程.

线程的划分尺度⼩于进程(资源⽐进程少),使得多线程程序的并发性⾼。

进程在执⾏过程中拥有独⽴的内存单元,⽽多个线程共享内存,从⽽极 ⼤地提⾼了程序的运⾏效率

线线程不能够独⽴执⾏,必须依存在进程中

优缺点

线程和进程在使⽤上各有优缺点:线程执⾏开销⼩,但不利于资源的管理和 保护;⽽进程正相反。

线程锁Lock

缺点 就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量 的混乱 (即线程⾮安全)

怎么样解决线程非安全这个问题呢?这里就引出了线程锁的概念

    当多个线程⼏乎同时修改某⼀个共享数据的时候,需要进⾏同步控制
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引⼊互 斥锁。
互斥锁为资源引⼊⼀个状态:锁定/⾮锁定。

某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他 线程不能更改;直到该线程释放资源,将资源的状态变成“⾮锁定”,其他的 线程才能再次锁定该资源。互斥锁保证了每次只有⼀个线程进⾏写⼊操作, 从⽽保证了多线程情况下数据的正确性。

threading模块中定义了Lock类,可以⽅便的处理锁定;

from threading import Thread, Lock
import time

g_num = 0


def test1():
    global g_num
    # 这个线程和test2线程都在抢着 对这个锁 进行上锁,如果有1方成功的上锁,那么导致另外
    # 一方会堵塞(一直等待)到这个锁被解开为止
    mutex.acquire()
    for i in range(1000000):
        g_num += 1
    mutex.release()  # 用来对mutex指向的这个锁 进行解锁,,,只要开了锁,那么接下来会让所有因为
    # 这个锁 被上了锁 而堵塞的线程 进行抢着上锁

    print("---test1---g_num=%d" % g_num)


def test2():
    global g_num
    mutex.acquire()
    for i in range(1000000):
        g_num += 1
    mutex.release()

    print("---test2---g_num=%d" % g_num)


# 创建一把互斥锁,这个锁默认是没有上锁的
mutex = Lock()

p1 = Thread(target=test1)
p1.start()
p2 = Thread(target=test2)
p2.start()

print("---g_num=%d---" % g_num)

运行结果:

C:\Users\Admin\PycharmProjects\BlockChain\venv\Scripts\python.exe C:/Users/Admin/PycharmProjects/BlockChain/Thread/threadingtest.py
---g_num=172105---
---test1---g_num=1000000
---test2---g_num=2000000

  总结:

  锁的好处:
     确保了某段关键代码只能由⼀个线程从头到尾完整地执⾏,这里就不会出现如果一个线程更改完数据后,没有生效时,而第二个线程再次更改数据,避免了多线程共享数据的出错。
锁的坏处:

      阻⽌了多线程并发执⾏,包含锁的某段代码实际上只能以单线程模式执 ⾏,效率就⼤⼤地下降了 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对⽅持有 的锁时,可能会造成死锁

使用线程互斥锁是解决了多线程共享数据可能出差的问题,但是还有可能会造成死锁。

死锁

     在线程间共享多个资源的时候,如果两个线程分别占有⼀部分资源并且同时 等待对⽅的资源,就会造成死锁。尽管死锁很少发⽣,但⼀旦发⽣就会造成应⽤的停⽌响应。下⾯看⼀个死锁 的例⼦。

import threading
import time


class MyThread1(threading.Thread):
    def run(self):
        if mutexA.acquire():
            print(self.name + '----do1---up----')
            time.sleep(1)
        if mutexB.acquire(): # mutexB在等待MyTherad2中 mutexBjeisu
            print(self.name + '----do1---down----')
        mutexB.release() #释放 mutexB锁
        mutexA.release() #释放 mutexA


class MyThread2(threading.Thread):
    def run(self):
        if mutexB.acquire():
            print(self.name + '----do2---up----')
        time.sleep(1)
        if mutexA.acquire():# mutexA在等待MyTherad1中 mutexA解锁
            print(self.name + '----do2---down----')
        mutexA.release() # 释放mutexA锁
        mutexB.release() # 释放mutexB锁


mutexA = threading.Lock()
mutexB = threading.Lock()

if __name__ == '__main__':
    t1 = MyThread1()
    t2 = MyThread2()
    t1.start()
    t2.start()

运行结果:

C:\Users\Admin\PycharmProjects\BlockChain\venv\Scripts\python.exe C:/Users/Admin/PycharmProjects/BlockChain/Thread/threadingtest.py
Thread-1----do1---up----
Thread-2----do2---up----

通过上面的例子可以看出,双反都在等待对方锁的释放,执行结果就没有再往下执行,这就造成了死锁。程序会一直在处于等待的状态。

怎么样来避免这种死锁的发生?这里引入同步的概念

同步

同步就是让多个进程有序的执行,从而避免死锁的发生。例

from threading import Thread,Lock
from time import sleep

class Task1(Thread):
    def run(self):
        while True:
            if lock1.acquire():
                print("------Task 1 -----")
                sleep(0.5)
                lock2.release()

class Task2(Thread):
    def run(self):
        while True:
            if lock2.acquire():
                print("------Task 2 -----")
                sleep(0.5)
                lock3.release()

class Task3(Thread):
    def run(self):
        while True:
            if lock3.acquire():
                print("------Task 3 -----")
                sleep(0.5)
                lock1.release()

#使用Lock创建出的锁默认没有“锁上”
lock1 = Lock()
#创建另外一把锁,并且“锁上”
lock2 = Lock()
lock2.acquire()
#创建另外一把锁,并且“锁上”
lock3 = Lock()
lock3.acquire()

t1 = Task1()
t2 = Task2()
t3 = Task3()

t1.start()
t2.start()
t3.start()

在创建锁时,将其他两把锁上锁,只让一个线程来执行,执行完成后接着把下一把锁解开,让三个线程有序的执行。

Python中,多线程是一种并发编程技术,允许一个程序同时执行多个任务,每个任务在单独的线程中运行。使用Python的内置`threading`模块可以轻松地创建和管理线程。以下是使用Python多线程的基本步骤: 1. 导入`threading`模块:这是多线程编程的第一步。 ```python import threading ``` 2. 定义线程函数(target):你需要为每个线程定义一个函数,这个函数将在新线程中执行。 ```python def worker_function(arg): # 线程执行的代码 pass ``` 3. 创建线程对象:使用`Thread`类实例化一个线程,将定义的函数作为参数传递。 ```python thread = threading.Thread(target=worker_function, args=(arg,)) ``` 4. 启动线程:调用`start()`方法启动线程,此时线程会在后台运行。 ```python thread.start() ``` 5. 等待线程完成:如果你需要等待线程结束,可以使用`join()`方法。 ```python thread.join() # 如果你想让主线程等待这个线程结束 ``` 6. 错误处理:线程可能遇到异常,使用`threading`模块提供的异常处理器来捕获并处理。 ```python try: thread.start() except Exception as e: print(f"Error in thread: {e}") ``` 需要注意的是,Python中的全局解释器锁(GIL)限制了同一时间只能有一个线程在执行Python字节码。这意味着多线程并不能真正实现并行计算,但在IO密集型任务中仍然很有用,比如网络请求、文件读写等。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值