多线程:
_thread模块
例一
import _thread
import time
# 为线程定义一个函数
def print_time(threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print("%s: %s" % (threadName, time.ctime(time.time())))
# 创建两个线程
try:
_thread.start_new_thread(print_time, ("Thread-1", 2,))
_thread.start_new_thread(print_time, ("Thread-2", 4,))
except:
print("Error: 无法启动线程")
while 1:
pass
例二:
import _thread
from time import sleep
from datetime import datetime
date_time_format='%y-%M-%d %H:%M:%S'
def date_time_str(date_time):
return datetime.strftime(date_time, date_time_format)
def loop_one():
print(f'+++线程一开始于:{date_time_str(datetime.now())}')
print('+++线程一休眠4秒')
sleep(4)
print(f'+++线程一休眠结束,结束于:{date_time_str(datetime.now())}')
def loop_two():
print(f'***线程二开始时间:{date_time_str(datetime.now())}')
print('***线程二休眠2秒')
sleep(2)
print(f'***线程二休眠结束,结束时间:{date_time_str(datetime.now())}')
def main():
print(f'------所有线程开始时间:{date_time_str(datetime.now())}')
_thread.start_new_thread(loop_one, ())
_thread.start_new_thread(loop_two, ())
sleep(6)
print(f'------所有线程结束时间:{date_time_str(datetime.now())}')
if __name__ == '__main__':
main()
主线程一旦运行结束,就关闭运行着的其他两个线程。这可能造成主线程过早或过晚退出,这时就要使用线程锁,主线程可以在两个子线程都退出后立即退出。 示例代码如下
import _thread
from time import sleep
from datetime import datetime
loops=[4, 2]
date_time_format='%y-%M-%d %H:%M:%S'
def date_time_str(date_time):
return datetime.strftime(date_time, date_time_format)
def loop(n_loop, n_sec, lock):
print(f'线程({n_loop})开始执行:{date_time_str(datetime.now())},先休眠({n_sec})秒')
sleep(n_sec)
print(f'线程({n_loop})休眠结束,结束于:{date_time_str(datetime.now())}')
lock.release()
def main():
print('---所有线程开始执行...')
locks=[]
n_loops=range(len(loops))
for i in n_loops:
lock=_thread.allocate_lock()
lock.acquire()
locks.append(lock)
for i in n_loops:
_thread.start_new_thread(loop, (i, loops[i], locks[i]))
for i in n_loops:
while locks[i].locked(): pass
print(f'---所有线程执行结束:{date_time_str(datetime.now())}')
if __name__ == '__main__':
main()
threading模块:
_thread模块不支持守护线程,当主线程退出时,所有子线程无论是否在工作,都会被强行退出。threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求,就一直等着。如果设定一个线程为守护线程,就表示这个线程不重要,在进程退出时,不用等待这个线程退出。如果主线程退出时不用等待子线程完成,就要设定这些线程的daemon属性,即在线程Thread.start()开始前,调用setDaemon()函数设定线程的daemon标志(Thread.setDaemon(True)),表示这个线程“不重要”。如果一定要等待子线程执行完成再退出主线程,就什么都不用做或显式调用Thread.setDaemon(False)以保证daemon标志为False,可以调用Thread.isDaemon()函数判断daemon标志的值。新的子线程会继承父线程的daemon标志,主线程在所有非守护线程退出后才会结束,即进程中没有非守护线程存在时才结束。
例子:
import threading
from time import sleep
from datetime import datetime
loops=[4, 2]
date_time_format='%y-%M-%d %H:%M:%S'
def date_time_str(date_time):
return datetime.strftime(date_time, date_time_format)
def loop(n_loop, n_sec):
print(f'线程({n_loop})开始执行:{date_time_str(datetime.now())},先休眠({n_sec})秒')
sleep(n_sec)
print(f'线程({n_loop})休眠结束,结束于:{date_time_str(datetime.now())}')
def main():
print(f'---所有线程开始执行:{date_time_str(datetime.now())}')
threads=[]
n_loops=range(len(loops))
for i in n_loops:
t=threading.Thread(target=loop, args=(i, loops[i]))
threads.append(t)
for i in n_loops: # start threads
threads[i].start()
for i in n_loops: # wait for all
threads[i].join() # threads to finish
print(f'---所有线程执行结束于:{date_time_str(datetime.now())}')
if __name__ == '__main__':
main()
附自己修改后的代码:
import threading
from time import sleep
from datetime import datetime
loops=[2, 2]
date_time_format='%y-%M-%d %H:%M:%S'
def date_time_str(date_time):
return datetime.strftime(date_time, date_time_format)
def loop(n_loop, n_sec):
print(f'线程({n_loop})开始执行:{date_time_str(datetime.now())},先休眠({n_sec})秒')
sleep(n_sec)
print(f'线程({n_loop})休眠结束,结束于:{date_time_str(datetime.now())}')
def begin(n_loop,sec):
print(f'线程{n_loop}开始执行:{date_time_str(datetime.now())}')
print("begin")
sleep(sec)
print(f'{n_loop}休眠结束,结束于:{date_time_str(datetime.now())}')
def main():
print(f'---所有线程开始执行:{date_time_str(datetime.now())}')
threads=[]
n_loops=range(len(loops))
t=threading.Thread(target=loop, args=(1, loops[0]))
threads.append(t)
m=threading.Thread(target=begin,args=(2,loops[1]))
threads.append(m)
for i in n_loops: # start threads
threads[i].start()
for i in n_loops: # wait for all
threads[i].join() # threads to finish
print(f'---所有线程执行结束于:{date_time_str(datetime.now())}')
if __name__ == '__main__':
main()
线程同步(LOCK和RLOCK)
如果多个线程共同修改某个数据,就可能会出现不可预料的结果。为了保证数据的正确性,需要对多个线程进行同步。 使用Thread对象的Lock和RLock可以实现简单的线程同步,这两个对象都有acquire方法和release方法。对于每次只允许一个线程操作的数据,可以将操作放到acquire和release方法之间。 多线程的优势在于可以同时运行多个任务,但当线程需要共享数据时,可能存在数据不同步的问题。 考虑这样一种情况:一个列表里所有元素都是0,线程set从后向前把所有元素改成1,而线程print负责从前往后读取列表并输出。
线程set开始改的时候,线程print可能就来输出列表了,输出就成了一半0一半1,这就是数据不同步的问题。为了避免这种情况,引入了锁的概念。 锁有两种状态——锁定和未锁定。当一个线程(如set)要访问共享数据时,必须先获得锁定;如果已经有别的线程(如print)获得锁定了,就让线程set暂停,也就是同步阻塞;等到线程print访问完毕,释放锁以后,再让线程set继续。 经过这样的处理,输出列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。
示例代码:
import threading
from time import sleep
from datetime import datetime
date_time_format='%y-%M-%d %H:%M:%S'
class MyThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID=threadID
self.name=name
self.counter=counter
def run(self):
print(f"开启线程: {self.name}")
# 获取锁,用于线程同步
threadLock.acquire()
print_time(self.name, self.counter, 3)
# 释放锁,开启下一个线程
threadLock.release()
def date_time_str(date_time):
return datetime.strftime(date_time, date_time_format)
def print_time(threadName, delay, counter):
while counter:
sleep(delay)
print(f"{threadName}: {date_time_str(datetime.now())}")
counter -= 1
def main():
# 创建新线程
thread1=MyThread(1, "Thread-1", 1)
thread2=MyThread(2, "Thread-2", 2)
# 开启新线程
thread1.start()
thread2.start()
# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)
# 等待所有线程完成
for t in threads:
t.join()
print("退出主线程")
if __name__ == "__main__":
threadLock=threading.Lock()
threads=[]
main()
和mysql交互:
增加和查询操作:
import pymysql
import time
pymysql.install_as_MySQLdb()
from sqlalchemy import create_engine,text
#定义数据库属性
hostname = '127.0.0.1'
port = '3308'
database = 'data1'
username = 'heziyi'
pwd = '123456'
dburl = 'mysql+mysqldb://{}:{}@{}:{}/{}'.format(username,pwd,hostname,port,database)
timenow= time.strftime('%Y-%m-%d',time.localtime(time.time()))
print(timenow)
#创建数据库连接对象
engine = create_engine(dburl,echo=True)
with engine.connect() as con:
# rs = con.execute('SELECT 1') # con.execute(text("select 1 "))
sql = 'INSERT INTO data1.wash VALUES(10,\'15\',\'175\',\'9\',\'19.4\',\'2021-05-24\');'
result = con.execute(
sql
)
result = con.execute('select * from wash')
print(result.fetchall())
查询某一列:
result = con.execute('select times,average,Time from wash')
print(result.fetchall())
结果:
[(‘4’, 30.5, datetime.date(2021, 5, 18)), (‘5’, 28.4, datetime.date(2021, 5, 20)), (‘6’, 26.7, datetime.date(2021, 5, 21)), (‘7’, 26.0, datetime.date(2021, 5, 22)), (‘8’, 25.7, datetime.date(2021, 5, 23)), (‘9’, 19.4, datetime.date(2021, 5, 24))]
格式化插入(传入变量的方式):
timenow= time.strftime('%Y-%m-%d',time.localtime(time.time()))
#创建数据库连接对象
engine = create_engine(dburl,echo=True)
with engine.connect() as con:
sql = 'INSERT INTO data1.wash VALUES(11,\'15\',\'175\',\'9\',\'19.4\',\'{}\');'.format(timenow)
附:python的格式化输出
多个格式化
‘b’ - 二进制。将数字以2为基数进行输出。
‘c’ - 字符。在打印之前将整数转换成对应的Unicode字符串。
‘d’ - 十进制整数。将数字以10为基数进行输出。
‘o’ - 八进制。将数字以8为基数进行输出。
‘x’ - 十六进制。将数字以16为基数进行输出,9以上的位数用小写字母。
‘e’ - 幂符号。用科学计数法打印数字。用’e’表示幂。
‘g’ - 一般格式。将数值以fixed-point格式输出。当数值特别大的时候,用幂形式打印。
‘n’ - 数字。当值为整数时和’d’相同,值为浮点数时和’g’相同。不同的是它会根据区域设置插入数字分隔符。
‘%’ - 百分数。将数值乘以100然后以fixed-point(‘f’)格式打印,值后面会有一个百分号。
print('{0:b}'.format(3))
print('{0:d}'.format(3))
print('{0:o}'.format(3))
print('{0:x}'.format(3))
name = ‘jack’
age = 18
sex = ‘man’
job = “IT”
salary = 9999.99
print(f’my name is {name.capitalize()}.’)
print(f’I am {age:*^10} years old.’)
print(f’I am a {sex}’)
print(f’My salary is {salary:10.3f}’)
结果
my name is Jack.
I am 18 years old.
I am a man
My salary is 9999.990
附录
关于python运行多线程的问题:
1)Python多线程的效率怎么样?
答:Python有全锁局的存在(同一时间只能有一个线程执行),并不能利用多核优势。如果你的多线程进程是CPU密集型的,多线程就不能带来效率的提升,相反还可能因为线程的频繁切换导致效率下降。如果是IO密集型,多线程进程就可以利用IO阻塞等待时的空闲时间执行其他线程,从而提升效率。 (2)既然Python解释器是单线程的,还有进行多线程编程的必要吗?
答:多线程最开始不是用来解决多核利用率问题的,而是用来解决IO占用时CPU闲置的问题。 多线程可以用来解决阻塞问题,可以做事件响应机制(或者类似信号槽的问题)。如果运行瓶颈不是在CPU运算而是在IO(网络)上,多线程显然很划算。 能产生IO阻塞的情况很多,如网络、磁盘等。当发生阻塞时,Python是不耗CPU的,此时如果只有一个线程就没法处理其他事情了。对于有IO阻塞的环境,多线程可能让你的CPU跑到100%。 另一个用处来自于Python的C扩展模块。在扩展模块里可以释放GIL。释放GIL期间不应该调用任何Python API。对于一些非常繁重的计算,可以写成C模块,计算前释放GIL,计算后重新申请GIL,并将结果返回给Python。这样就可以让Python进程利用更多CPU资源。每个Python线程都是OS级别的Pthread线程。利用Python管理这些线程比在C层级操作Pthread更方便。