1、
当一个程序启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程(Main Thread),因为它是程序开始时就执行的,如果你需要再创建线程,那么创建的线程就是这个主线程的子线程。每个进程至少都有一个主线程,在Winform中,应该就是创建GUI的线程。
主线程的重要性体现在两方面:1.是产生其他子线程的线程;2.通常它必须最后完成执行比如执行各种关闭动作。
实验代码:
import threading
def f(i):
print("I am from a thread,num=%d\n"%(i));
def main():
for i in range(1,10):
t=threading.Thread(target=f,args=(i,));#TypeError: f() argument after * must be a sequence, not int
t.setDaemon(True);
t.start();
if __name__=="__main__":
main();
实验结果为:
结论:虽然线程的创建和启动是有顺序的,但线程是并发运行的,所以那个线程先执行完是不确定的。从运行结果可以看到,输出的数字也是没有规律的。而且在“I am from a thread, num=4",前面还有个>>>,是说明主程序在此处已经退出。
线程对象的setDaemon()方法可以让子线程随着主线程的退出而结束,不过注意的是setDaemon()方法必须在线程对象没有调用start()方法之前调用(默认情况下;在python
中,主线程结束后,会默认等待子线程结束后,主线程才退出)。
2、阻塞进程
# -*- coding: cp936 -*-
import threading
def f(i):
print("I am from a thread,num=%d\n"%(i));
def main():
for i in range(1,10):
t=threading.Thread(target=f,args=(i,));#TypeError: f() argument after * must be a sequence, not int
t.setDaemon(True);
t.start();
t.join();#可以阻塞进程直到线程执行完毕。
if __name__=="__main__":
main();
运行结果为:
可以看到,进程在所有线程结束后才退出。
3、指令锁
当多个线程同时访问同一资源(比如,全局变量),可能会出现访问冲突。
冲突:
import threading
import time
num=0;
def f():
global num;
b=num;
time.sleep(0.0001);
num=b+1;
print('%s\n'%threading.currentThread().getName());
def main():
for i in range(1,20):
t=threading.Thread(target=f);
t.setDaemon(True);
t.start();
t.join();
print(num);
if __name__=="__main__":
main();
实验结果为:
改进:
可以使用锁来限制线程同时访问同一资源。指令锁处于锁定状态时,不能被特定的线程所拥有。当线程申请一个处于锁定状态的锁时线程会被阻塞,直至该锁被释放。
在访问全局变量之前申请一个指令锁,在访问全局变量之后释放一个指令锁,这样就可以避免多个线程同时访问全局变量。
# -*- coding: cp936 -*-
import threading
import time
lock=threading.Lock();#创建一个指令锁
num=0;
def f():
global num;
if lock.acquire():
print('%s获得指令锁.'%threading.currentThread().getName());
b=num;
time.sleep(0.0001);
num=b+1;
lock.release()#释放指令锁
print('%s释放指令锁.'%threading.currentThread().getName());
print('%s\n'%threading.currentThread().getName());
def main():
for i in range(1,20):
t=threading.Thread(target=f);
t.setDaemon(True);
t.start();
t.join();
print(num);
if __name__=="__main__":
main();
实验结果:
4、可重入锁
使用指令锁可以避免多个线程同时访问全局变量。但是如果一个线程里面有递归函数,则它可能会多次请求访问全局变量,此时,即使线程已经获得指令锁,在它再次申请
指令锁时会被阻塞。每个可重入锁都关联一个请求计数器和一个占有它的线程,当请求计数器为0时,这个锁可以被一个线程请求得到并把锁的请求计数加1。如果同一个线程
再次请求这个锁,请求计数器就会增加,当该线程释放RLock时,其计数器减1,当计数器为0时,该锁被释放。
实验代码:
# -*- coding: cp936 -*-
import threading
import time
lock=threading.RLock();#创建一个可重入锁
num=0;
def f():
global num;
#第一次请求锁定
if lock.acquire():
print('%s获得指令锁.'%threading.currentThread().getName());
time.sleep(0.0001);
#第二次请求锁定
if lock.acquire():
print('%s获得指令锁.'%threading.currentThread().getName());
time.sleep(0.0001);
lock.release()#释放指令锁
print('%s释放指令锁.'%threading.currentThread().getName());
time.sleep(0.0001);
print('%s释放指令锁.'%threading.currentThread().getName());
lock.release()#释放指令锁
def main():
for i in range(1,20):
t=threading.Thread(target=f);
t.setDaemon(True);
t.start();
t.join();
print(num);
if __name__=="__main__":
main();
实验结果:
>>>
Thread-1获得指令锁.
Thread-1获得指令锁.
Thread-1释放指令锁.
Thread-1释放指令锁.
Thread-2获得指令锁.
Thread-2获得指令锁.
Thread-2释放指令锁.
Thread-2释放指令锁.
Thread-3获得指令锁.
Thread-3获得指令锁.
Thread-3释放指令锁.
Thread-3释放指令锁.
Thread-4获得指令锁.
Thread-4获得指令锁.
Thread-4释放指令锁.
Thread-4释放指令锁.
Thread-5获得指令锁.
Thread-5获得指令锁.
Thread-5释放指令锁.
Thread-5释放指令锁.
Thread-6获得指令锁.
Thread-6获得指令锁.
Thread-6释放指令锁.
Thread-6释放指令锁.
Thread-7获得指令锁.
Thread-7获得指令锁.
Thread-7释放指令锁.
Thread-7释放指令锁.
Thread-8获得指令锁.
Thread-8获得指令锁.
Thread-8释放指令锁.
Thread-8释放指令锁.
Thread-9获得指令锁.
Thread-9获得指令锁.
Thread-9释放指令锁.
Thread-9释放指令锁.
Thread-10获得指令锁.
Thread-10获得指令锁.
Thread-10释放指令锁.
Thread-10释放指令锁.
Thread-11获得指令锁.
Thread-11获得指令锁.
Thread-11释放指令锁.
Thread-11释放指令锁.
Thread-12获得指令锁.
Thread-12获得指令锁.
Thread-12释放指令锁.
Thread-12释放指令锁.
Thread-13获得指令锁.
Thread-13获得指令锁.
Thread-13释放指令锁.
Thread-13释放指令锁.
Thread-14获得指令锁.
Thread-14获得指令锁.
Thread-14释放指令锁.
Thread-14释放指令锁.
Thread-15获得指令锁.
Thread-15获得指令锁.
Thread-15释放指令锁.
Thread-15释放指令锁.
Thread-16获得指令锁.
Thread-16获得指令锁.
Thread-16释放指令锁.
Thread-16释放指令锁.
Thread-17获得指令锁.
Thread-17获得指令锁.
Thread-17释放指令锁.
Thread-17释放指令锁.
Thread-18获得指令锁.
Thread-18获得指令锁.
Thread-18释放指令锁.
Thread-18释放指令锁.
Thread-19获得指令锁.
Thread-19获得指令锁.
Thread-19释放指令锁.
Thread-19释放指令锁.
0
>>>
5、信号量