一、多任务
多任务简单来说就是操作系统可以同时运行多个任务(同一时间进行多件事)。
并行:真的多任务——多个CPU在同一个时间点执行多个任务;
并发:假的多任务——CPU交替在同一时间段执行多个任务,并不是同时执行,只是因为CPU执行的速度过快,使得人们感到是在“同时”执行,执行的先后顺序取决于各个程序对于时间片资源的争夺;
【
几乎所有的操作系统都支持同时运行多个任务,每个任务通常是一个程序,每一个运行中的程序就是一个进程,即进程是应用程序的执行实例。现代的操作系统几乎都支持多进程并发执行。
并发和并行是两个概念,并行指在同一时刻有多条指令在多个处理器上同时执行;并发是指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。
但事实的真相是,对于一个 CPU 而言,在某个时间点它只能执行一个程序。也就是说,只能运行一个进程,CPU 不断地在这些进程之间轮换执行。那么,为什么用户感觉不到任何中断呢?
这是因为相对人的感觉来说,对于一个 CPU 而言,在某个时间点它只能执行一个程序。也就是说,只能运行一个进程,CPU 不断地在这些进程之间轮换执行。那么,为什么用户感觉不到任何中断呢?
这是因为相对人的感觉来说,CPU 的执行速度太快了(如果启动的程序足够多,则用户依然可以感觉到程序的运行速度下降了)。所以,虽然 CPU 在多个进程之间轮换执行,但用户感觉到好像有多个进程在同时执行。
线程是进程的组成部分,一个进程可以拥有多个线程。在多线程中,会有一个主线程来完成整个进程从开始到结束的全部操作,而其他的线程会在主线程的运行过程中被创建或退出。
当进程被初始化后,主线程就被创建了,对于绝大多数的应用程序来说,通常仅要求有一个主线程,但也可以在进程内创建多个顺序执行流,这些顺序执行流就是线程。
当一个进程里只有一个线程时,叫作单线程。超过一个线程就叫作多线程。
每个线程必须有自己的父进程,且它可以拥有自己的堆栈、程序计数器和局部变量,但不拥有系统资源,因为它和父进程的其他线程共享该进程所拥有的全部资源。线程可以完成一定的任务,可以与其他线程共享父进程中的共享变量及部分环境,相互之间协同完成进程所要完成的任务。
多个线程共享父进程里的全部资源,会使得编程更加方便,需要注意的是,要确保线程不会妨碍同一进程中的其他线程。
线程是独立运行的,它并不知道进程中是否还有其他线程存在。线程的运行是抢占式的,也就是说,当前运行的线程在任何时候都可能被挂起,以便另外一个线程可以运行。
多线程也是并发执行的,即同一时刻,Python 主程序只允许有一个线程执行,这和全局解释器锁有关系,
一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发运行。
从逻辑的角度来看,多线程存在于一个应用程序中,让一个应用程序可以有多个执行部分同时执行,但操作系统无须将多个线程看作多个独立的应用,对多线程实现调度和管理以及资源分配,线程的调度和管理由进程本身负责完成。
简而言之,进程和线程的关系是这样的:操作系统可以同时执行多个任务,每一个任务就是一个进程,进程可以同时执行多个任务,每一个任务就是一个线程。
】
【下面 转自别人】
打个比方。并发,就像一个人(cpu)喂2个孩子(程序),轮换着每人喂一口,表面上两个孩子都在吃饭。并行,就是2个人喂2个孩子,两个孩子也同时在吃饭。
二、线程
线程(有时候成为轻量级进程),他们是在同一个进程之下执行的,并共享相同的上下文;
线程包括开始,执行顺序和结束三个部分。它有一个指令指针,用于记录当前运行的上下文。当其他县城运行时,它可以被抢占(中断)和临时挂起(也称为睡眠)——这样的做法叫做让步。
一个进程中的各个线程和主线程共享一片数据空间,因此相比于独立的进程而言,线程间的信息共享和通信更加容易。线程一般是以并发方式进行的,正是因为这种并发和数据共享机制,是的多任务间的协作成为可能。当然,在单核CPU系统中,真正的并发是不可能的,所以是这样运行的:每个线程运行一会儿,然后让步给其他进程(再次排队等待更多的CPU时间)。
当然,这样的共享并不是没有风险的。如果两个或者多个线程访问同一片数据,由于数据的访问顺序不同,可能导致的结果也是不一样的,这种情况通常称为竞态条件。
线程无法给予公平的执行时间,这个是因为一些函数会在完成前保持阻塞状态,如果没有专门为多线程情况进行修改,会导致CPU的时间分配向这些贪婪的函数倾斜。
线程执行是没有先后顺序的。
- function - 线程函数。
- args - 传递给线程函数的参数,他必须是个tuple类型。
__author__ = 'Administrator'
import time
import threading
def sing():
for i in range(5):
print("————第%d秒正在唱歌——" %i)
time.sleep(1)
def dance():
for i in range(5):
print("————第%d秒正在跳舞——" %i)
time.sleep(1)
def main():
t1=threading.Thread(target=sing)
t2=threading.Thread(target=dance)
t1.start()
t2.start()
if __name__ == '__main__':
main()
#############################
————第0秒正在唱歌——
————第0秒正在跳舞——
————第1秒正在跳舞——
————第1秒正在唱歌——
————第2秒正在唱歌——
————第2秒正在跳舞——
————第3秒正在唱歌——
————第3秒正在跳舞——
————第4秒正在唱歌——
————第4秒正在跳舞——
View Code
让某些线程先执行:
__author__ = 'Administrator'
import time
import threading
def test1():
for i in range(5):
print("————test1—%d—" %i)
time.sleep(1)
def test2():
for i in range(5):
print("————test2—%d—" %i)
time.sleep(1)
def main():
t1=threading.Thread(target=test1)
t2=threading.Thread(target=test2)
t1.start()
time.sleep(1)
print("——1————————————")
t2.start()
time.sleep(1)
print("——2————————————")
print(threading.enumerate())
if __name__ == '__main__':
main()
################################
————test1—0—
————test1—1—
——1————————————
————test2—0—
————test2—1—
————test1—2—
——2————————————
[<Thread(Thread-1, started 4380)>, <_MainThread(MainThread, started 7052)>, <Thread(Thread-2, started 6660)>]
————test2—2—
————test1—3—
————test2—3—
————test1—4—
————test2—4—
View Code
循环查看当前执行的线程:
__author__ = 'Administrator'
import time
import threading
def test1():
for i in range(5):
print("————test1—%d—" %i)
time.sleep(1)
def test2():
for i in range(5):
print("————test2—%d—" %i)
time.sleep(1)
def main():
t1=threading.Thread(target=test1)
t2=threading.Thread(target=test2)
t1.start()
time.sleep(1)
print("——1————————————")
t2.start()
time.sleep(1)
print("——2————————————")
while True:
print(threading.enumerate())
if len(threading.enumerate())<=1:
break
time.sleep(1)
if __name__ == '__main__':
main()
##################################
————test1—0—
————test1—1—
——1————————————
————test2—0—
————test2—1—
————test1—2—
——2————————————
[<Thread(Thread-2, started 6224)>, <Thread(Thread-1, started 6436)>, <_MainThread(MainThread, started 1300)>]
————test1—3—
————test2—2—
[<Thread(Thread-2, started 6224)>, <Thread(Thread-1, started 6436)>, <_MainThread(MainThread, started 1300)>]
————test2—3—
————test1—4—
[<Thread(Thread-2, started 6224)>, <Thread(Thread-1, started 6436)>, <_MainThread(MainThread, started 1300)>]
————test2—4—
[<Thread(Thread-2, started 6224)>, <_MainThread(MainThread, started 1300)>]
[<_MainThread(MainThread, started 1300)>]
View Code
验证创建线程以及运行时间:
__author__ = 'Administrator'
import time
import threading
def test():
for i in range(5):
print("————%d——" %i)
def main():
print(threading.enumerate())
t=threading.Thread(target=test)
print(threading.enumerate())
t.start()
print("*"*100)
print(threading.enumerate())
#结论:当调用Thread的时候,不会创建线程;
# 当调用Thread创建出来的实例对象的start方法的时候,才会创建线程以及让这个线程开始运行
# 线程结束是有函数决定,函数结束,线程结束
# 子线程先结束,主线程最后结束
if __name__ == '__main__':
main()
##############################
[<_MainThread(MainThread, started 4132)>]
[<_MainThread(MainThread, started 4132)>]
————0——
****************************************************************************************************
[<Thread(Thread-1, started 4868)>, <_MainThread(MainThread, started 4132)>]
————1——
————2——
————3——
————4——
View Code
创建线程的第二种方法:
通过继承Thread类创建线程:
通过继承 Thread 类来创建并启动线程的步骤如下:
- 定义 Thread 类的子类,并重写该类的 run() 方法。run() 方法的方法体就代表了线程需要完成的任务,因此把 run() 方法称为线程执行体。
- 创建 Thread 子类的实例,即创建线程对象。
- 调用线程对象的 start() 方法来启动线程。
__author__ = 'Administrator'
import time
import threading
class MyThread(threading.Thread):
def run(self):
for i in range(5):
time.sleep(1)
#name苏醒中保存的是当前线程的名字
msg="我是"+self.name+"@"+str(i)
print(msg)
if __name__ == '__main__':
print(threading.enumerate())
t=MyThread()#创建类对象
#t=threading.Thread(target=test)
#target=函数名
t.start()
print(threading.enumerate())
#######################
[<_MainThread(MainThread, started 1124)>]
[<MyThread(Thread-1, started 1504)>, <_MainThread(MainThread, started 1124)>]
我是Thread-1@0
我是Thread-1@1
我是Thread-1@2
我是Thread-1@3
我是Thread-1@4
View Code
__author__ = 'Administrator'
import time
import threading
class MyThread(threading.Thread):
def run(self):
self.eat()
self.bark()
for i in range(5):
time.sleep(1)
#name苏醒中保存的是当前线程的名字
msg="我是"+self.name+"@"+str(i)
print(msg)
def eat(self):
print("这个是吃的方法")
def bark(self):
print("这个是叫的方法")
if __name__ == '__main__':
print(threading.enumerate())
t=MyThread()#创建类对象
#t=threading.Thread(target=test)
#target=函数名
t.start()#类中必须要有run方法
print(threading.enumerate())
##############################
[<_MainThread(MainThread, started 2752)>]
这个是吃的方法
[<_MainThread(MainThread, started 2752)>, <MyThread(Thread-1, started 3800)>]
这个是叫的方法
我是Thread-1@0
我是Thread-1@1
我是Thread-1@2
我是Thread-1@3
我是Thread-1@4
View Code
__author__ = 'Administrator'
import time
import threading
class MyThread(threading.Thread):
# def run(self):
# self.eat()
# self.bark()
# for i in range(5):
# time.sleep(1)
# #name苏醒中保存的是当前线程的名字
# msg="我是"+self.name+"@"+str(i)
# print(msg)
def eat(self):
print("这个是吃的方法")
def bark(self):
print("这个是叫的方法")
if __name__ == '__main__':
print(threading.enumerate())
t=MyThread()#创建类对象
#t=threading.Thread(target=test)
#target=函数名
t.start()#类中必须要有run方法
print(threading.enumerate())
##############################
[<_MainThread(MainThread, started 4600)>]
[<_MainThread(MainThread, started 4600)>]
View Code
mport threading
def func(num):
for i in range(num):
print(threading.current_thread().getName()+''+str(i))
for i in range(5):
if i==3:
t1=threading.Thread(target=func,args=(5,))
t1.start()
t2 = threading.Thread(target=func, args=(10,))
t2.start()
'''
threading.current_thread():它是 threading 模块的函数,该函数总是返回当前正在执行的线程对象。
getName():它是 Thread 类的实例方法,该方法返回调用它的线程名字。
在 Threading 模块中,除了 current_thread() 函数外,还经常使用如下 2 个函数:
threading.enumerate():返回一个正运行线程的 list。“正运行”是指线程处于“启动后,且在结束前”状态,不包括“启动前”和“终止后”状态。
threading.activeCount():返回正在运行的线程数量。与
View Code
虽然上面程序只显式创建并启动了两个线程,但实际上程序有三个线程,即程序显式创建的两个子线程和主线程。前面己经提到,当 Python 程序开始运行后,程序至少会创建一个主线程,主线程的线程执行体就是程序中的主程序(没有放在任何函数中的代码)
注意,启动线程使用 start() 方法,而不是 run() 方法。调用 start() 方法来启动线程,系统会把该 run() 方法当成线程执行体来处理;但如果直接调用线程对象的 run() 方法,则 run() 方法立即就会被执行,而且在该方法返回之前其他线程无法并发执行。也就是说,如果直接调用线程对象的 run() 方法,则系统会把线程对象当成一个普通对象,而 run() 方法是一个普通方法,不是线程执行体。
import threading
def func(num):
for i in range(num):
print(threading.current_thread().getName()+''+str(i))
for i in range(5):
if i==3:
# 直接调用线程对象的run()方法
# 系统会把线程对象当成普通对象,把run()方法当成普通方法
# 所以下面两行代码并不会启动两个线程,而是依次执行两个run()方法
t1=threading.Thread(target=func,args=(5,)).run()
# t1.start()
t2 = threading.Thread(target=func, args=(10,)).run()
# t2.start()
View Code
直接调用了线程对象的 run() 方法,程序运行的结果是整个程序只有一个主线程。还有一点需要指出,如果直接调用线程对象的 run() 方法,则在 run() 方法中不能直接通过 name 属性(getName() 方法)来获取当前执行线程的名字,而是需要使用 threading.current_thread() 函数先获取当前线程,然后再调用线程对象的 name 属性来获取线程的名字。
线程的新建和就绪状态:
新建状态,不要再次调用线程对象的 start() 方法。在调用线程对象的 run() 方法之后,该线程己经不再处于新建状态,不要再次调用线程对象的 start() 方法。在调用线程对象的 start() 方法之后,该线程立即进入就绪状态(相当于“等待执行”),但该线程并未真正进入运行状态。
线程的运行和堵塞状态:
如果处于就绪状态的线程获得了 CPU,开始执行 run() 方法的线程执行体,则该线程处于运行状态。
如果计算机只有一个 CPU,那么在任何时刻只有一个线程处于运行状态。当然,在一个具有多处理器的机器上,将会有多个线程并行(Parallel)执行;当线程数大于处理器数时,依然会存在多个线程在同一个 CPU 上轮换的情况。
当一个线程开始运行后,它不可能一直处于运行状态(除非它的线程执行体足够短,瞬间就执行结束了),线程在运行过程中需要被中断,目的是使其他线程获得执行的机会,线程调度的细节取决于底层平台所采用的策略。对于采用抢占式调度策略的系统而言,系统会给每一个可执行的线程一个小时间段来处理任务;当该时间段用完后,系统就会剥夺该线程所占用的资源,让其他线程获得执行的机会。在选择下一个线程时,系统会考虑线程的优先级。
所有现代的桌面和服务器操作系统都采用抢占式调度策略,但一些小型设备如手机等则可能采用协作式调度策略,在这样的系统中,只有当一个线程调用了它的 sleep() 或 yield() 方法后才会放弃其所占用的资源(也就是必须由该线程主动放弃其所占用的资源)。
当发生如下情况时,线程将会进入阻塞状态:
- 线程调用 sleep() 方法主动放弃其所占用的处理器资源。
- 线程调用了一个阻塞式 I/O 方法,在该方法返回之前,该线程被阻塞。
- 线程试图获得一个锁对象,但该锁对象正被其他线程所持有。关于锁对象的知识,后面将有更深入的介绍。
- 线程在等待某个通知(Notify)。
当前正在执行的线程被阻塞之后,其他线程就可以获得执行的机会。被阻塞的线程会在合适的时候重新进入就绪状态,注意是就绪状态,而不是运行状态。也就是说,被阻塞线程的阻塞解除后,必须重新等待线程调度器再次调度它。
针对上面几种情况,当发生如下特定的情况时可以解除阻塞,让该线程重新进入就绪状态:
- 调用 sleep() 方法的线程经过了指定的时间。
- 线程调用的阻塞式 I/O 方法己经返回。
- 线程成功地获得了试图获取的锁对象。
- 线程正在等待某个通知时,其他线程发出了一个通知。
线程死亡
线程会以如下方式结束,结束后就处于死亡状态:
- run() 方法或代表线程执行体的 target 函数执行完成,线程正常结束。
- 线程抛出一个未捕获的 Exception 或 Error。
注意,当主线程结束时,其他线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,不会受主线程的影响。
为了测试某个线程是否己经死亡,可以调用线程对象的 is_alive() 方法,当线程处于就绪、运行、阻塞三种状态时,该方法将返回 True;当线程处于新建、死亡两种状态时,该方法将返回 False。
不要试图对一个已经死亡的线程调用 start() 方法使它重新启动,死亡就是死亡,该线程将不可再次作为线程运行。
# 定义action函数准备作为线程执行体使用
def func(max):
for i in range(50):
print(threading.current_thread().name + " " + str(i))
# 创建线程对象
t = threading.Thread(target=func, args=(100,))
for i in range(100):
# 调用threading.current_thread()函数获取当前线程
print(threading.current_thread().name + " " + str(i))
if i == 20:
# 启动线程
t.start()
# 判断启动后线程的is_alive()值,输出True
print(t.is_alive())
# 当线程处于新建、死亡两种状态时,is_alive()方法返回False
# 当i > 20时,该线程肯定已经启动过了,如果sd.is_alive()为False时
# 那就是死亡状态了
# if i > 50 and not(sd.is_alive()):
# # 试图再次启动该线程
# t.start()
View Code
多线程共享全局变量:
__author__ = 'Administrator'
import time
import threading
g_num=50
def test1():
global g_num
g_num+=1
print("=======test1——g_num=%d" %g_num)
def test2():
print("=======test2——g_num=%d" %g_num)
def main():
t1=threading.Thread(target=test1)
t2=threading.Thread(target=test2)
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print(g_num)
if __name__ == '__main__':
main()
#####################
=======test1——g_num=51
=======test2——g_num=51
51
View Code
__author__ = 'Administrator'
import time
import threading
g_num=[11,22,33,44]
def test1(temp):
temp.append(555)
print("=======test1——temp=%s" %str(temp))
def test2(temp):
print("=======test2——temp=%s" %str(temp))
def main():
t1=threading.Thread(target=test1,args=(g_num,))#元祖里面的逗号不能少
t2=threading.Thread(target=test2,args=(g_num,))
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print(g_num)
if __name__ == '__main__':
main()
##################################
=======test1——temp=[11, 22, 33, 44, 555]
=======test2——temp=[11, 22, 33, 44, 555]
[11, 22, 33, 44, 555]
View Code
共享全局变量问题——资源竞争
多线程的优势在于并发性,即可以同时运行多个任务。但是当线程需要使用共享数据时,也可能会由于数据不同步产生“错误情况”,【注意,此处说的是有可能,并不是一定】这是由系统的线程调度具有一定的随机性造成的。
互斥锁的作用就是解决数据不同步问题。
__author__ = 'Administrator'
import time
import threading
g_num=0
def test1(temp):
for i in range(temp):
global g_num
g_num+=1
print("=======test1——g_num=%d" %g_num)
def test2(temp):
for i in range(temp):
global g_num
g_num+=1
print("=======test2——g_num=%d" %g_num)
def main():
t1=threading.Thread(target=test1,args=(100,))
t2=threading.Thread(target=test2,args=(100,))
t1.start()
t2.start()
time.sleep(5)
print(g_num)
if __name__ == '__main__':
main()
##################
=======test1——g_num=100
=======test2——g_num=200
200
View Code
__author__ = 'Administrator'
import time
import threading
g_num=0
def test1(temp):
for i in range(temp):
global g_num
g_num+=1
print("=======test1——g_num=%d" %g_num)
def test2(temp):
for i in range(temp):
global g_num
g_num+=1
print("=======test2——g_num=%d" %g_num)
def main():
t1=threading.Thread(target=test1,args=(1000000,))
t2=threading.Thread(target=test2,args=(1000000,))
t1.start()
t2.start()
time.sleep(5)
print(g_num)
if __name__ == '__main__':
main()
############################
=======test2——g_num=1177009
=======test1——g_num=1192154
1192154
View Code
解决资源竞争问题:
互斥锁:【转自别人】
当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制;
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
也就是换句话说,在多线程时,保证修改共享数据时是有序的修改,不会产生数据修改的混乱。
某个线程要更改共享数据是,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;知道该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入 操作,从而拨正了多线程情况下的数据的正确性。
上锁解锁过程:
当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。
每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。
线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
总结:
锁的好处:
确保了某段关键代码只能由一个线程从头到尾完整地执行。
锁的坏处:
阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了;
由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁。
__author__ = 'Administrator'
import time
import threading
g_num=0
#创建一个互斥锁,默认是没有上锁的
mutex=threading.Lock()
def test1(temp):
global g_num
#上锁,如果之前没有被上锁,那么此时上锁成功
#如果上锁之前,已经被上锁了,那么此时会堵塞在这里,直到这个所被揭开
mutex.acquire()
for i in range(temp):
g_num+=1
# 解锁
mutex.release()
print("=======test1——g_num=%d" %g_num)
def test2(temp):
global g_num
mutex.acquire()
for i in range(temp):
g_num+=1
mutex.release()
print("=======test2——g_num=%d" %g_num)
def main():
t1=threading.Thread(target=test1,args=(1000000,))
t2=threading.Thread(target=test2,args=(1000000,))
t1.start()
t2.start()
time.sleep(2)
print(g_num)
if __name__ == '__main__':
main()
###############################
=======test1——g_num=1000000
=======test2——g_num=2000000
2000000
View Code
__author__ = 'Administrator'
import time
import threading
g_num=0
#创建一个互斥锁,默认是没有上锁的
mutex=threading.Lock()
def test1(temp):
global g_num
#上锁,如果之前没有被上锁,那么此时上锁成功
#如果上锁之前,已经被上锁了,那么此时会堵塞在这里,直到这个所被揭开
for i in range(temp):
mutex.acquire()
g_num+=1
# 解锁
mutex.release()
print("=======test1——g_num=%d" %g_num)
def test2(temp):
global g_num
mutex.acquire()
for i in range(temp):
g_num+=1
mutex.release()
print("=======test2——g_num=%d" %g_num)
def main():
t1=threading.Thread(target=test1,args=(1000000,))
t2=threading.Thread(target=test2,args=(1000000,))
t1.start()
t2.start()
time.sleep(2)
print(g_num)
if __name__ == '__main__':
main()
##################################
=======test2——g_num=1015809
=======test1——g_num=2000000
2000000
View Code
死锁:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。——来自百度百科
__author__ = 'Administrator'
import socket
import threading
def recv_msg(udp_socket):
while True:
# 3.接收数据
recv_data=udp_socket.recvfrom(1024)
print(recv_data)
def send_msg(udp_socket,dest_ip,dest_port):
while True:
# 4.发送数据
send_data=input("请输入想要发送的信息:")
udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port))
def main():
# 1.创建套接字
udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 2.绑定ip和port
dest_ip=input("请输入要链接的对方的ip:")
dest_port=int(input("请输入要链接的对方的端口号:"))
#创建线程
t_recv_msg=threading.Thread(target=recv_msg,args=(udp_socket,))
t_send_msg=threading.Thread(target=send_msg,args=(udp_socket,dest_ip,dest_port,))
t_recv_msg.start()
t_send_msg.start()
if __name__ == '__main__':
main()
View Code
__author__ = 'Administrator'
import time
import threading
g_num=[11,22,33,44]
def test1(temp):
temp.append(555)
print("=======test1——temp=%s" %str(temp))
def test2(temp):
print("=======test2——temp=%s" %str(temp))
def main():
t1=threading.Thread(target=test1,args=(g_num,))
t2=threading.Thread(target=test2,args=(g_num,))
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print(g_num)
if __name__ == '__main__':
main()