网络编程之线程

网络编程之线程

什么是线程?

 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程
  线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程
      车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线
      流水线的工作需要电源,电源就相当于cpu
        
  **所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。**

  多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。

      例如,北京地铁与上海地铁是不同的进程,而北京地铁里的13号线是一个线程,北京地铁所有的线路共享北京地铁所有的资源,比如所有的乘客可以被所有线路拉。

进程的开销小

创建进程的开销要远大于线程?

如果我们的软件是一个工厂,该工厂有多条流水线,流水线工作需要电源,电源只有一个即cpu(单核cpu)
一个车间就是一个进程,一个车间至少一条流水线(一个进程至少一个线程)
创建一个进程,就是创建一个车间(申请空间,在该空间内建至少一条流水线)
而建线程,就只是在一个车间内造一条流水线,无需申请空间,所以创建开销小

进程之间是竞争关系,线程之间是协作关系?
车间直接是竞争/抢电源的关系,竞争(不同的进程直接是竞争关系,是不同的程序员写的程序运行的,迅雷抢占其他进程的网速,360把其他进程当做病毒干死)
一个车间的不同流水线式协同工作的关系(同一个进程的线程之间是合作关系,是同一个程序写的程序内开启动,迅雷内的线程是合作关系,不会自己干自己)

为何要用多线程

多线程指的是,在一个进程中开启多个线程,简单的讲:如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程。详细的讲分为4点:

  1. 多线程共享一个进程的地址空间

      2. 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用

      3. 若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。

      4. 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销要小的多。(这一条并不适用于python)

线程模型

多个线程共享同一个进程的地址空间中的资源,是对一台计算机上多个进程的模拟,有时也称线程为轻量级的进程

  而对一台计算机上多个进程,则共享物理内存、磁盘、打印机等其他物理资源。

  多线程的运行也多进程的运行类似,是cpu在多个线程之间的快速切换。

 不同的进程之间是充满敌意的,彼此是抢占、竞争cpu的关系,如果迅雷会和QQ抢资源。而同一个进程是由一个程序员的程序创建,所以同一进程内的线程是合作关系,一个线程可以访问另外一个线程的内存地址,大家都是共享的,一个线程干死了另外一个线程的内存,那纯属程序员脑子有问题。

  类似于进程,每个线程也有自己的堆栈

不同于进程,线程库无法利用时钟中断强制线程让出CPU,可以调用thread_yield运行线程自动放弃cpu,让另外一个线程运行。

  

  线程通常是有益的,但是带来了不小程序设计难度,线程的问题是:

扫描二维码关注公众号,回复: 7547632 查看本文章

  1. 父进程有多个线程,那么开启的子线程是否需要同样多的线程

   如果是,那么附近中某个线程被阻塞,那么copy到子进程后,copy版的线程也要被阻塞吗,想一想nginx的多线程模式接收用户连接。

  2. 在同一个进程中,如果一个线程关闭了问题,而另外一个线程正准备往该文件内写内容呢?

​ 如果一个线程注意到没有内存了,并开始分配更多的内存,在工作一半时,发生线程切换,新的线程也发现内存不够用了,又开始分配更多的内存,这样内存就被分配了多次,这些问题都是多线程编程的典型问题,需要仔细思考和设计。

开启线程的两种方式

from threading import Thread
import time

def task(name):
    print('%s is running'%name)
    time.sleep(3)
    print('%s is over'%name)
# 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
t = Thread(target=task,args=('egon',))
t.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
# 小的代码执行完 线程就已经开启了
print('主')
from threading import Thread
import time

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

    def run(self):  # 自定义线程必须是run
        print('%s is running'%self.name)
        time.sleep(3)
        print('%s is over'%self.name)

t = MyThread('机车')
t.start()
print('主')

线程对象及其他方法

# 线程是没有主子之分的,只是认为定义的。
from threading import Thread,current_thread,active_count
import time
import os

def task(name,i):
    # print('%s is running'%name)
    print('子current_thread:',current_thread().name) # 查看线程的名字
    print('子',os.getpid())
    # time.sleep(i)

    print('%s is over'%name)
# 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
t = Thread(target=task,args=('egon',1))
t1 = Thread(target=task,args=('jason',2))
t.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
t1.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
t1.join()  # 主线程等待子线程运行完毕
print('当前正在活跃的线程数',active_count())
# 小的代码执行完 线程就已经开启了
print('主')
# print('主current_thread:',current_thread().name) # mainthread
print('主',os.getpid())

'''
子current_thread: Thread-1
子 2844
子current_thread: Thread-2
子 2844
jason is over
当前正在活跃的线程数 1  # t1子进程设置睡眠时间2s,等到2s结束,线程t也已经运行结束,就一个主线程是活跃的;如果将线程t设置join,活跃的线程就是2.
主
egon is over
主
主 2844
'''

守护进程

from threading import Thread,current_thread
import time



def task(i):
    print(current_thread().name)
    time.sleep(i)
    print('GG')
# for i in range(3):
#     t = Thread(target=task,args=(i,))
#     t.start()
t = Thread(target=task,args=(1,))
t.daemon = True
t.start()
print('主')


# 主线程运行结束之后需要等待子线程结束才能结束呢?
"""
主线程的结束也就意味着进程的结束
主线程必须等待其他非守护线程的结束才能结束
(意味子线程在运行的时候需要使用进程中的资源,而主线程一旦结束了资源也就销毁了)

线程间的数据是共享的(都是享用同一进程下的数据)

from threading import Thread,current_thread
import time
import os

x=100
def task(i):
    global x
    x-=1
    print(f'这是第{i}次减值,x={x},线程名是{current_thread().name},线程号是{os.getpid()}')
    time.sleep(0.1)

for i in range(1,101):
    t = Thread(target=task,args=(i,))
    t.start()
print(x)
print(f'主:线程名是{current_thread().name},线程号是{os.getpid()}')

'''
这是第1次减值,x=99,线程名是Thread-1,线程号是5332
这是第2次减值,x=98,线程名是Thread-2,线程号是5332
这是第3次减值,x=97,线程名是Thread-3,线程号是5332
这是第4次减值,x=96,线程名是Thread-4,线程号是5332
.
.
.
0
主:线程名是MainThread,线程号是5332
'''

线程互斥锁

from threading import Thread,Lock
import time
import os

from threading import Thread,Lock
import time
import os

# 不加锁的状态
x=100
def task():
    global x
    temp = x
    time.sleep(0.1)  # 不加锁所有进程在这里睡眠0.1s之后,同时(并发)去修改x=100,都把100做减1的操作,造成数据错乱。
    x = temp-1

start = time.time()
l=[]
mutex = Lock()
for i in range(100):
    t = Thread(target=task)
    l.append(t)
    t.start()
for t in l:
    t.join()

print('zhu')
print(x)
print('总运行时间为:',time.time()-start)
'''
zhu
99
总运行时间为: 0.11800670623779297
'''

# 加锁之后
x=100
def task():
    mutex.acquire() # 加锁之后使者一百个线程由并发(切换+保存状态)变成了串行取修改x的值,
    global x
    temp = x
    time.sleep(0.1)
    x = temp-1
    mutex.release()

start = time.time()
l=[]
mutex = Lock()
for i in range(100):
    t = Thread(target=task)
    l.append(t)
    t.start()
for t in l:
    t.join()

print('zhu')
print(x)
print('总运行时间为:',time.time()-start)

'''
zhu
0
总运行时间为: 10.003572225570679
'''
from threading import Thread
from multiprocessing import Process
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")

if __name__ == '__main__':
    t1=Thread(target=foo)
    t2=Thread(target=bar)
    t1.daemon=True
    t1.start()
    t2.start()
    print("main-------")
# 主线程会等待非守护的子线程结束才会结束,这里线程t2运行结束,t1早都已经运行结束,所以。。。。。。。
 '''
 123
456
main-------
end123
end456
 '''

猜你喜欢

转载自www.cnblogs.com/zhangchaocoming/p/11723671.html