【6.0】多线程操作

【一】threading模块介绍

  • multiprocess模块的完全模仿了threading模块的接口
  • 二者在使用层面,有很大的相似性,因而不再详细介绍

官网链接https://docs.python.org/3/library/threading.html?highlight=threading

【二】开启线程的两种方式

  • 开启线程不需要在main下面执行代码,直接书写即可
  • 但是我们还是习惯性的将启动命令写在main下面

img

【1】方式一:直接调用 Thread 方法

from multiprocessing import Process
from threading import Thread
import time


def task(name):
    print(f'当前任务:>>>{name} 正在运行')
    time.sleep(3)
    print(f'当前任务:>>>{name} 结束运行')


def Thread_main():
    t = Thread(target=task, args=("dream",))
    # 创建线程的开销非常小,几乎代码运行的一瞬间线程就已经创建了
    t.start()
    '''
    当前任务:>>>dream 正在运行this is main process!
    this is main process!
    当前任务:>>>dream 结束运行
    '''


def Process_main():
    p = Process(target=task, args=("dream",))
    p.start()
    '''
    this is main process!
    当前任务:>>>dream 正在运行
    当前任务:>>>dream 结束运行
    '''


if __name__ == '__main__':
    Thread_main()
    # Process_main()
    print('this is main process!')

【2】方式二:继承 Thread 父类

from threading import Thread
import time


class MyThread(Thread):

    def __init__(self, name):
        # 重写了别人的方法,又不知道别人的方法里面有什么, 就调用父类的方法
        super().__init__()
        self.name = name

    # 定义 run 函数
    def run(self):
        print(f'{self.name} is running')
        time.sleep(3)
        print(f'{self.name} is ending')


def main():
    t = MyThread('dream')
    t.start()
    print(f'this is a main process')

    """
    dream is running
    this is a main process
    dream is ending
    """


if __name__ == '__main__':
    main()

【三】一个进程下开启多个线程和多个子进程的区别

【1】谁的开启速度快

from threading import Thread
from multiprocessing import Process
import time


def work():
    print('hello')


def timer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        print(f'函数 {func.__name__} 运行时间为:{time.time() - start_time}')
        return res

    return inner


@timer
def work_process():
    # 在主进程下开启子进程
    t = Process(target=work)
    t.start()
    print('主线程/主进程')
    '''
    主线程/主进程
    函数 work_process 运行时间为:0.0043752193450927734
    hello
    '''


@timer
def work_thread():
    # 在主进程下开启线程
    t = Thread(target=work)
    t.start()
    print('主线程/主进程')
    '''
    打印结果:
    hello
    主线程/主进程
    函数 work_thread 运行时间为:0.0001499652862548828
    '''


if __name__ == '__main__':
    # part1 : 多线程
    work_thread()
    # part2 : 多进程
    work_process()

【2】查看pid

from threading import Thread
from multiprocessing import Process
import os


def work():
    print('hello', os.getpid())


def work_thread():
    # part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
    t1 = Thread(target=work)
    t2 = Thread(target=work)
    t1.start()
    t2.start()
    print('主线程/主进程pid', os.getpid())

    # hello 5022
    # hello 5022
    # 主线程/主进程pid 5022


def work_process():
    # part2:开多个进程,每个进程都有不同的pid
    p1 = Process(target=work)
    p2 = Process(target=work)
    p1.start()
    p2.start()
    print('主线程/主进程pid', os.getpid())

    # 主线程/主进程pid 5032
    # hello 5034
    # hello 5035


if __name__ == '__main__':
    # part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
    work_thread()
    # part2:开多个进程,每个进程都有不同的pid
    work_process()

【3】同一进程内的线程共享该进程的数据?

from threading import Thread
from multiprocessing import Process


def work():
    global n
    n = 0


def work_process():
    n = 100
    p = Process(target=work)
    p.start()
    p.join()
    print('主', n)  # 毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100

    # 主 100


def work_thread():
    n = 1
    t = Thread(target=work)
    t.start()
    t.join()
    print('主', n)  # 查看结果为1,因为同一进程内的线程之间共享进程内的数据


if __name__ == '__main__':
    # part1 多进程 : 子进程只改自己的
    work_process()
    # part2 多线程: 数据发生错乱,同一进程内的线程之间共享数据
    work_thread()

【四】多线程并发的socket服务端

【1】基础版(单服务端)

(1)客户端

from socket import *

# 不写参数:默认是TCP协议
# (1)创建客户端对象
client = socket()

# (2)绑定 IP PORT
# (2)建立链接桥梁 --(呼应客户端的 ip 和 port)
IP = '127.0.0.1'
PORT = 8082
client.connect((IP, PORT))

# (4)链接循环
while True:
    # (4.1)向服务端发数据
    msg_to_server = b'this is a message'
    client.send(msg_to_server)

    # 接受服务器返回的数据
    data_from_server = client.recv(1024)

    # (4.3)接收到客户端的信息
    msg_from_client = data_from_server.decode('utf-8')
    print(msg_from_client)

(2)服务端

from threading import Thread
from multiprocessing import Process
from socket import *

'''
服务端的三大特点:
    (1)要有固定的IP和PORT
    (2)24h不间断提供服务
    (3)能够支持并发
'''

# 不写参数:默认是TCP协议
# (1)创建服务器对象
server = socket()

# (2)建立链接桥梁 --(呼应客户端的 ip 和 port)
IP = '127.0.0.1'
PORT = 8082
server.bind((IP, PORT))

# (3)半连接池创建
server.listen(5)


# 正常版本
def normal_version():
    # (4)链接循环
    while True:
        # (4.1) 接受连接对象和 ip port
        conn, addr = server.accept()
        while True:
            # 捕获异常并抛出
            try:
                msg_from_client = conn.recv(1024)
                # (4.2)接受的信息为空时,会无限循环
                if len(msg_from_client) == 0:
                    break

                # (4.3)接收到客户端的信息
                msg_from_client = msg_from_client.decode('utf-8')
                print(msg_from_client)

                # (4.4)返回给客户端信息
                msg_to_client = msg_from_client.upper()
                msg_to_client = msg_to_client.encode('utf-8')
                conn.send(msg_to_client)

            except Exception as e:
                print(e)
                break
        # (4.5)关闭链接
        conn.close()


# 将接受处理数据部分封装成函数调用
def talk(conn):
    while True:
        # 捕获异常并抛出
        try:
            msg_from_client = conn.recv(1024)
            # (4.2)接受的信息为空时,会无限循环
            if len(msg_from_client) == 0:
                break

            # (4.3)接收到客户端的信息
            msg_from_client = msg_from_client.decode('utf-8')
            print(msg_from_client)

            # (4.4)返回给客户端信息
            msg_to_client = msg_from_client.upper()
            conn.send(msg_to_client)

        except Exception as e:
            print(e)
            break
    # (4.5)关闭链接
    conn.close()



if __name__ == '__main__':
    normal_version()

【2】升级版(多服务端)

(1)服务端

from threading import Thread
from multiprocessing import Process
from socket import *

'''
服务端的三大特点:
    (1)要有固定的IP和PORT
    (2)24h不间断提供服务
    (3)能够支持并发
'''

# 不写参数:默认是TCP协议
# (1)创建服务器对象
server = socket()

# (2)建立链接桥梁 --(呼应客户端的 ip 和 port)
IP = '127.0.0.1'
PORT = 8083
server.bind((IP, PORT))

# (3)半连接池创建
server.listen(5)


# 正常版本
def normal_version():
    # (4)链接循环
    while True:
        # (4.1) 接受连接对象和 ip port
        conn, addr = server.accept()
        while True:
            # 捕获异常并抛出
            try:
                msg_from_client = conn.recv(1024)
                # (4.2)接受的信息为空时,会无限循环
                if len(msg_from_client) == 0:
                    break

                # (4.3)接收到客户端的信息
                msg_from_client = msg_from_client.decode('utf-8')
                print(msg_from_client)

                # (4.4)返回给客户端信息
                msg_to_client = msg_from_client.upper()
                msg_to_client = msg_to_client.encode('utf-8')
                conn.send(msg_to_client)

            except Exception as e:
                print(e)
                break
        # (4.5)关闭链接
        conn.close()


# 将接受处理数据部分封装成函数调用
def talk(conn):
    while True:
        # 捕获异常并抛出
        try:
            msg_from_client = conn.recv(1024)
            # (4.2)接受的信息为空时,会无限循环
            if len(msg_from_client) == 0:
                break

            # (4.3)接收到客户端的信息
            msg_from_client = msg_from_client.decode('utf-8')
            print(msg_from_client)

            # (4.4)返回给客户端信息
            msg_to_client = msg_from_client.upper()
            msg_to_client = msg_to_client.encode('utf-8')
            conn.send(msg_to_client)

        except Exception as e:
            print(e)
            break
    # (4.5)关闭链接
    conn.close()


# 多线程版本
def threading_version(conn):
    t = Thread(target=talk, args=(conn,))
    t.start()


# 多进程版本
def process_version(conn):
    p = Process(target=talk, args=(conn,))
    p.start()


def main_t():
    # (4)链接循环
    while True:
        # (4.1) 接受连接对象和 ip port
        conn, addr = server.accept()
        threading_version(conn)


def main_p():
    # (4)链接循环
    while True:
        # (4.1) 接受连接对象和 ip port
        conn, addr = server.accept()
        process_version(conn)


if __name__ == '__main__':
    main_t()

(2)客户端

from socket import *

# 不写参数:默认是TCP协议
# (1)创建客户端对象
client = socket()

# (2)绑定 IP PORT
# (2)建立链接桥梁 --(呼应客户端的 ip 和 port)
IP = '127.0.0.1'
PORT = 8083
client.connect((IP, PORT))

# (4)链接循环
while True:
    # (4.1)向服务端发数据
    msg_to_server = b'this is a message'
    client.send(msg_to_server)

    # 接受服务器返回的数据
    data_from_server = client.recv(1024)

    # (4.3)接收到客户端的信息
    msg_from_client = data_from_server.decode('utf-8')
    print(msg_from_client)

【五】线程对象的 join 方法

from threading import Thread
import time


def task(name):
    print(f'the task {name} is beginning')
    time.sleep(3)
    print(f'the task {name} is ending')


def main():
    t = Thread(target=task, args=('dream',))
    t.start()

    # 主线程等待子进程结束之后再运行
    t.join()

    print(f'the task is main task')


if __name__ == '__main__':
    main()
    
    # the task dream is beginning
    # the task dream is ending
    # the task is main task

【六】同一个进程下的多个线程之间数据是共享的

from threading import Thread
import time

money = 999


def task():
    global money
    money = 99
    print(f'task中的money:>>>>{money}')


def main():
    print(f'子进程之前的money:>>>>{money}')
    t = Thread(target=task)
    t.start()
    print(f'子进程之后的money:>>>>{money}')


if __name__ == '__main__':
    main()

    # 子进程之前的money:>>>>999
    # task中的money:>>>>99
    # 子进程之后的money:>>>>99

【七】线程对象属性及其他方法

【1】同一个进程下的进程号相同

from threading import Thread
import time, os


def task():
    print(f'this is a task PID {os.getpid()}')


def main():
    t = Thread(target=task)
    t.start()
    print(f'this is a main PID {os.getpid()}')


if __name__ == '__main__':
    main()
    
    # this is a task PID 6496
    # this is a main PID 6496

【2】获取当前进程的名字current_thread

from threading import Thread, active_count, current_thread
import time, os


def task():
    # 获取当前线程的名字
    print(f'this is a task name {current_thread().name}')


def main():
    t = Thread(target=task)
    t.start()
    print(f'this is a main name {current_thread().name}')


if __name__ == '__main__':
    main()

    # this is a task name Thread-1
    # this is a main name MainThread

【3】统计当前活跃的线程数active_count

  • 这里统计的线程数 2 个
  • 可能已经有一个子线程已经死了
from threading import Thread, active_count, current_thread
import time, os


def task():
    # 获取当前线程的名字
    print(f'this is a task name {current_thread().name}')


def main():
    t = Thread(target=task)
    t1 = Thread(target=task)
    t.start()
    t1.start()
    # 统计当前活跃的线程数
    print(f'this is a main process active_process {active_count()}')
    print(f'this is a main name :>>>> {current_thread().name}')


if __name__ == '__main__':
    main()

    # this is a task name Thread-1
    # this is a task name Thread-2
    # this is a main process active_process 1
    # this is a main name :>>>> MainThread
  • 这里统计的活跃进程数是 3 个
  • 让我们第一个子进程晚一点死
from threading import Thread, active_count, current_thread
import time, os


def task():
    # 获取当前线程的名字
    print(f'this is a task name {current_thread().name}')
    time.sleep(1)


def main():
    t = Thread(target=task)
    t1 = Thread(target=task)
    t.start()
    t1.start()
    # 统计当前活跃的线程数
    print(f'this is a main process active_process {active_count()}')
    print(f'this is a main name :>>>> {current_thread().name}')


if __name__ == '__main__':
    main()

    # this is a task name Thread-1
    # this is a task name Thread-2
    # this is a main process active_process 3
    # this is a main name :>>>> MainThread
posted @ 2024-01-23 14:25  Chimengmeng  阅读(10)  评论(0编辑  收藏  举报
/* */