码迷,mamicode.com
首页 > 编程语言 > 详细

并发编程之多线程

时间:2017-10-13 20:05:34      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:encoding   返回   方式   sed   targe   write   lob   xxx   空间   

阅读目录

一: 什么是线程

二: 多线程的特点,为什么用多线程

三: 开启线程,threading模块

四: python  GIL

 

什么是线程

 

  在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程

  线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程

      车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线

      流水线的工作需要电源,电源就相当于cpu

  所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

 

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

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

 

线程的特点,为什么用多线程

 

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

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

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

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

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

 

 开启线程  threading模块

 

1.开启线程的两种方式

技术分享
#方式一
from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print(%s say hello %name)

if __name__ == __main__:
    t=Thread(target=sayhi,args=(egon,))
    t.start()
    print(主线程)
方式一
技术分享
#方式二
from threading import Thread
import time
class Sayhi(Thread):  #继承父类Thread
    def __init__(self,name):    #可以定义自己的初始属性
        super().__init__()   #继承父类的__init__方法
        self.name=name
    def run(self):    #开启线程执行run里的内容
        time.sleep(2)
        print(%s say hello % self.name)


if __name__ == __main__:
    t = Sayhi(egon)
    t.start()
    print(主线程)
方式二

 

 

2.在一个进程下开启多个线程与在一个进程下开启多个子进程的区别

 

1-开启线程的速度更快

2-在主进程下开启多个线程,每个线程都跟主进程的pid一样

技术分享
from threading import Thread
from multiprocessing import Process
import os

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

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

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

 

3-同一进程内的线程共享该进程的数据

技术分享
from  threading import Thread
from multiprocessing import Process
import os
def work():
    global n
    n=0

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


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

 

 

3.实例  

1-实例一  :多线程并发的套接字通讯

技术分享
#_*_coding:utf-8_*_
#!/usr/bin/env python
import multiprocessing
import threading

import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((127.0.0.1,8080))
s.listen(5)

def action(conn):
    while True:
        data=conn.recv(1024)
        print(data)
        conn.send(data.upper())

if __name__ == __main__:

    while True:
        conn,addr=s.accept()


        p=threading.Thread(target=action,args=(conn,))
        p.start()
多线程并发的socket服务端
技术分享
#_*_coding:utf-8_*_
#!/usr/bin/env python


import socket

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((127.0.0.1,8080))

while True:
    msg=input(>>: ).strip()
    if not msg:continue

    s.send(msg.encode(utf-8))
    data=s.recv(1024)
    print(data)
客户端

 

2-实例二  :三个任务,一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件

技术分享
from threading import Thread
msg_l=[]
format_l=[]
def talk():
    while True:
        msg=input(>>: ).strip()
        if not msg:continue
        msg_l.append(msg)

def format_msg():
    while True:
        if msg_l:
            res=msg_l.pop()
            format_l.append(res.upper())

def save():
    while True:
        if format_l:
            with open(db.txt,a,encoding=utf-8) as f:
                res=format_l.pop()
                f.write(%s\n %res)

if __name__ == __main__:
    t1=Thread(target=talk)
    t2=Thread(target=format_msg)
    t3=Thread(target=save)
    t1.start()
    t2.start()
    t3.start()
View Code

 

 

4.线程相关的其他方法

Thread实例对象的方法
  # isAlive(): 返回线程是否活动的。
  # getName(): 返回线程名。
  # setName(): 设置线程名。

threading模块提供的一些方法:
  # threading.currentThread(): 返回当前的线程变量。
  # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
技术分享
from threading import Thread,current_thread,enumerate,activeCount
import os,time
n=100

def work():
    print(%s is ruuning %current_thread().getName())
if __name__ == __main__:
    t=Thread(target=work)
    t.start()
    t.join()
    print(t.is_alive()) #判断线程是否存货
    print(t.getName())  #获得线程名字
    print(current_thread().getName())   #获取主线程名字
    print(enumerate())  #返回一个列表,有哪些活跃的线程
    print(activeCount())    #活跃线程的数量
    print(主线程,n)
示例

 

主线程等待子线程结束

技术分享
from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print(%s say hello %name)

if __name__ == __main__:
    t=Thread(target=sayhi,args=(egon,))
    t.start()
    t.join()
    print(主线程)
    print(t.is_alive())
    ‘‘‘
    egon say hello
    主线程
    False
    ‘‘‘
View Code

 

 

 

5.守护线程

 

无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁

需要强调的是:运行完毕并非终止运行

#1.对主进程来说,运行完毕指的是主进程代码运行完毕

#2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕

详细解释:

#1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束,

#2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。

 

 

from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print(%s say hello %name)

if __name__ == __main__:
    t=Thread(target=sayhi,args=(egon,))
    t.daemon = True #必须在t.start()之前设置
    t.start()

    print(主线程)
    print(t.is_alive())
    ‘‘‘
    主线程
    True
    ‘‘‘ 
技术分享
from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

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


t1=Thread(target=foo)   #设置线程
t2=Thread(target=bar)   #设置线程

t1.daemon=True  #把t1设置成守护线程
t1.start()  #开启线程
t2.start()  #开启线程
print("main-------")

# 打印结果
# 123
# 456
# main-------
# end123
#主线程必须在所有非守护线程结束后才结束,主线程运行完后就不再等守护线程结束,因为主线程结束时守护线程已经结束,所以先打印end123,再打印end456
# end456
迷惑人的例子

 

 

6.Python GIL(Global Interpreter Lock)

>>>详细介绍

 

并发编程之多线程

标签:encoding   返回   方式   sed   targe   write   lob   xxx   空间   

原文地址:http://www.cnblogs.com/zhoujunhao/p/7662789.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!