线程相关知识点

一:线程理论

1.进程:是资源单位

  • 相当于是车间,负责给里面的线程提供所需的相应的资源

2.线程:是执行单位

  • 相当于是流水线,负责执行真正的功能

3.一个进程至少有一个线程

4.多进程

  • 需要开辟新的内存空间,需要重新拷贝代码,资源消耗量大

5.多线程

  • 不需要开辟内存空间,也不需要拷贝代码,资源消耗量小

二:开启线程的两种方式

方式一:
from threading import Thread
import time


def task():
    print('task开始执行')
    time.sleep(2)
    print('task执行结束')


if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    print('主线程')
"""
执行结果:
task开始执行
主线程
task执行结束
"""
方式二:
from threading import Thread
import time


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

    def run(self):
        print(f'{self.name}开始执行')
        time.sleep(2)
        print(f'{self.name}执行结束')


if __name__ == '__main__':
    obj = MyThread('xie')
    obj.start()
    print('主线程')

三:join方法

1.定义:

主线程等子线程结束之后才能执行

2.代码
from threading import Thread
import time


def task():
    print('task子线程开始执行')
    time.sleep(2)
    print('task子线程执行结束')


if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    t.join()
    print('主线程')

"""
执行结果:
task子线程开始执行
task子线程执行结束
主线程
"""

四:同一进程下线程之间数据是共享的

1.代码
from threading import Thread

money = 100
def dask():
    global money
    money = 10

if __name__ == '__main__':
    t = Thread(target=dask())
    print(money)  # 10

五:线程实现TCP服务端并发效果

1.服务端
import socket
from threading import Thread
from multiprocessing import Process


"""
1.要有固定的port he ip
2.24小时不间断的服务
3.能够支持并发
"""

server = socket.socket()
server.bind(('127.0.0.1', 8808))
server.listen(5)


# 将服务的代码单独封装成一个函数
def task(conn):
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0: break
            print(data.decode('utf8'))
            conn.send(data.upper())
        except ConnectionError as e:
            print(e)
            break
    conn.close()


while True:
    conn, addr = server.accept()
    t = Thread(target=task, args=(conn,))
    t.start()
2.客户端
import socket

client = socket.socket()
client.connect(('127.0.0.1', 8808))
while True:
    client.send('hello '.encode('utf8'))
    data = client.recv(1024)
    print(data.decode('utf8'))

六:线程中的方法

1.方法
  • 1.线程名:current_thread().name
  • 2.当前的线程号:os.getpid()
  • 3.当前线程的父线程号:os.getppid()
2.特点
  • 1.同一进程下的线程拥有相同的线程号
3.代码
from threading import Thread, current_thread
import os


def task1():
    print('task1的线程名是:%s' % current_thread().name)
    print('task1的线程号是:%s' % os.getpid())
    print('task1的父进程号是:%s' % os.getppid())

def task2():
    print('task2的线程名是:%s' % current_thread().name)
    print('task2的线程号是:%s' % os.getpid())

if __name__ == '__main__':
    t1 = Thread(target=task1)
    t2 = Thread(target=task1)
    t1.start()
    t2.start()
    print('主进程号是:%s' % os.getpid())
"""
task1的线程号是:19784
task1的父进程号是:10044
task1的线程名是:Thread-2
task1的线程号是:19784
主进程号是:19784
task1的父进程号是:10044
"""

七:守护线程

守护线程伴随着被守护的线程的结束而结束

from threading import Thread
import time

def task():
    print('子线程运行task函数')
    time.sleep(3)
    print('子线程运行task结束')


t = Thread(target=task)
# t.daemon = True
t.start()
# t.daemon = True
print('主线程')
"""
进程下所有的非守护线程结束 主线程(主进程)才能真正结束!!!
"""

八:GIL全局解释器锁

1.储备知识
  • python解释器也是由编程语言写出来的
  • Cpython 用C写出来的
  • Jpython 用Java写出来的
  • Pypython 用python写出来的
  • 最常用的就是Cpython(默认)
2.通过官方文档来解释GIL

# 官方文档对GIL的解释
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.)

"""
1.GIL的研究是Cpython解释器的特点 不是python语言的特点
2.GIL本质也是一把互斥锁
3.GIL的存在使得同一个进程下的多个线程无法同时执行(关键)
	言外之意:单进程下的多线程无法利用多核优势 效率低!!!
4.GIL的存在主要是因为cpython解释器中垃圾回收机制不是线程安全的
"""
参考群内截图 理解上述34解读
1.误解:python的多线程就是垃圾 利用不到多核优势
	python的多线程确实无法使用多核优势 但是在IO密集型的任务下是有用的
2.误解:既然有GIL 那么以后我们写代码都不需要加互斥锁
	不对 GIL只确保解释器层面数据不会错乱(垃圾回收机制)
	针对程序中自己的数据应该自己加锁处理
3.所有的解释型编程语言都没办法做到同一个进程下多个线程同时执行
ps:我们平时在写代码的时候 不需要考虑GIL 只在学习和面试阶段才考虑!!!

你可能感兴趣的:(网络编程,python零基础,python,linux,开发语言)