Geithon (8): многопоточность Python и очередь

Java Python Безопасность Язык программирования

Я давно не обновлял свой блог, и мне стало лень к празднику Весны дома -_-, но я обещаю вам закончить эту вводную серию по питону, а я продолжу усердно работать! желаю всем счастливого нового года и все будет хорошо!

концепция потока

Многие языки программирования, которые мы изучаем, такие как java, oc и т. д., имеют концепцию потоков. Потоки широко используются и приносят много удобства в нашу разработку. Они в основном используются для некоторой последовательной или параллельной логической обработки, например as Когда кнопка нажата, мы можем контролировать время выполнения потока с помощью индикатора выполнения для лучшего взаимодействия с пользователем.

Каждый независимый поток содержит вход в программу, последовательную последовательность выполнения и выход из программы.Поток должен существовать в программе, но не может работать независимо от программы!

Каждый поток имеет свой собственный набор памяти процессора, называемый контекстом потока, который отражает состояние регистров процессора на момент последнего запуска потока.Регистры указателя инструкций и указателя стека являются двумя наиболее важными регистрами в контексте потока. выполняется в контексте процесса, и эти адреса используются для маркировки памяти в адресном пространстве процесса, которому принадлежит поток.

Потоки Python

В Python в основном предусмотрены два модуля потока, поток и поток. Модуль потока предоставляет самые основные и низкоуровневые функции потока и простую блокировку. Модуль потока представляет собой расширенную инкапсуляцию модуля потока и предоставляет множество потоков. Свойства и методы Ниже мы проанализируем два модуля один за другим.

модуль потока (устаревший)

Часто используемые функциональные методы модуля потока:

Имя функции описывать
start_new_thread(function, args, kwargs=None) Создать новый поток, function — это имя функции, которая должна быть запущена потоком, args — параметр функции (тип кортежа), а kwargs — необязательный параметр.
allocate_lock() Выделите объект блокировки потока типа LockType
exit() выход потока
_count() Возвращает количество потоков, обратите внимание, что основной поток не включен, поэтому метод возвращает 0 при запуске метода в основном потоке.
locked блокировка locktype, возвращает true для блокировки
release() Снять блокировку объекта locktype
acquire() запирание

Возьмем пример:

import thread,time


def loop1():
	print '线程个数-' + str(thread._count())
	i=0
	try:
    	while i < 100:
        	print i
        	time.sleep(1)
        	i = i + 1
	except Exception as e:
    	print e


thread.start_new_thread(loop1,())

Запустив приведенный выше код, вы обнаружите, что печать цикла в методе loop1 не вызывается, а напрямую возвращает исключение:

Unhandled exception in thread started by 
sys.excepthook is missing
lost sys.stderr

В это время вы можете снова и снова проверять код, думая, что код неверен (да, этот человек — я), на самом деле, сам наш код не является неправильным, это дефект модуля потока раннего Python. (этот дефект также стал причиной этого. Основная причина, по которой модуль официально объявлен устаревшим): Когда мы используем start_new_thread для создания нового потока в основном потоке, основной поток не может знать, когда поток заканчивается, и он не знает, сколько ждать , так что основной поток завершил выполнение, дочерний поток не завершился, поэтому система выдает это исключение.

Есть два способа разрешить это исключение:

1. Позвольте основному потоку спать достаточно долго, чтобы дождаться, пока дочерний поток вернет результат:

import thread,time


def loop1():
	print '线程个数-' + str(thread._count())
	i=0
	try:
    	while i < 100:
        	print i
        	time.sleep(1)
        	i = i + 1
	except Exception as e:
    	print e


thread.start_new_thread(loop1,())
time.sleep(1000)   #让主线程休眠1000秒,足够子线程完成

2. Заблокируйте поток (ранние потоки Python используют общую обработку)

import thread,time


def loop1(lock):
	print '线程个数-' + str(thread._count())
	i=0
	try:
    	while i < 100:
        	print i
        	time.sleep(1)
        	i = i + 1
	except Exception as e:
    	lock.release()
    	print e

	lock.release()    #执行完毕,释放锁


lock=thread.allocate_lock()   #获取locktype对象
lock.acquire()   #锁定

thread.start_new_thread(loop1,(lock,))
while lock.locked():    #等待线程锁释放
	pass

Выше приведено обычное использование потока модуля потока. Мы видим, что работа с потоком, предоставляемая модулем потока, чрезвычайно ограничена и очень негибка в использовании. Давайте представим его аналогичный модуль потока.

Модуль для резьбы (рекомендуется)

Модуль threading — это совершенство работы с потоками, он имеет набор зрелых методов работы с потоками, которые могут в основном выполнять все необходимые нам операции с потоками.

Распространенные методы нарезки:

  • threading.currentThread(): возвращает текущую переменную потока.
  • threading.enumerate(): возвращает список запущенных потоков. Работает после запуска потока и до конца, исключая поток до начала и после конца.
  • threading.activeCount(): возвращает количество запущенных потоков, имеет тот же результат, что и len(threading.enumerate()).
  • run(): метод, используемый для представления активности потока.
  • start(): начать активность потока.
  • join([time]): Подождите, пока поток завершится. Это блокирует вызывающий поток до тех пор, пока метод потока join() не будет вызван для прерывания - выхода в обычном режиме или создания необработанного исключения - или до тех пор, пока не произойдет необязательный тайм-аут.
  • isAlive(): возвращает, активен ли поток.
  • getName(): возвращает имя потока.
  • setName(): установить имя потока.

Модуль для резьбы создает потоки двумя способами:

1. Создайте непосредственно путем инициализации объекта потока:

#coding=utf-8
import threading,time

def test():
	t = threading.currentThread()  # 获取当前子线程对象
	print t.getName()  # 打印当前子线程名字

	i=0
	while i<10:
    	print i
    	time.sleep(1)
    	i=i+1




m=threading.Thread(target=test,args=(),name='循环子线程')   #初始化一个子线程对象,target是执行的目标函数,args是目标函数的参数,name是子线程的名字
m.start()
t=threading.currentThread()   #获取当前线程对象,这里其实是主线程
print t.getName()   #打印当前线程名字,其实是主线程名字

Вы можете увидеть результат печати:

循环子线程
MainThread
0
1
2
3
4
5
6
7
8

2. Создается базовым классом потока

import threading,time
class myThread (threading.Thread):   #创建一个自定义线程类mythread,继承Thread

def __init__(self,name):
    """
    重新init方法
    :param name: 线程名
    """
    super(myThread, self).__init__(name=name)
    # self.lock=lock

    print '线程名'+name

def run(self):
    """
    重新run方法,这里面写我们的逻辑
    :return:
    """
    i=0
    while i<10:
        print i
        time.sleep(1)
        i=i+1


if __name__=='__main__':
	t=myThread('mythread')
	t.start()

вывод:

线程名线程
0
1
2
3
4
5
6
7
8
9

синхронизация потоков

Если два потока обращаются к одним и тем же данным одновременно, могут возникнуть непредсказуемые результаты.В настоящее время нам нужно использовать концепцию синхронизации потоков.

Выше мы говорили о модуле потока, уже использующем концепцию блокировки потока, объекты потока Lock и Rlock могут обеспечить простую синхронизацию потока, два объекта имеют методы получения и освобождения, для тех, кому нужно, позволяет только одну операцию данных потока, операция может быть помещена между методом получения и освобождения.

Давайте возьмем пример, нам нужно реализовать 3 потока для одновременного доступа к глобальной переменной и изменить эту переменную:

1. Разблокирован
import threading,time

lock=threading.Lock()   #全局的锁对象
temp=0    #我们要多线程访问的全局属性

class myThread (threading.Thread):   #创建一个自定义线程类mythread,继承Thread

	def __init__(self,name):
    	"""
    	重新init方法
    	:param name: 线程名
    	"""
    	super(myThread, self).__init__(name=name)
    	# self.lock=lock

    	print '线程名'+name

	def run(self):
    	"""
    	重新run方法,这里面写我们的逻辑
    	:return:
    	"""
    	global temp,lock

    	i=0
    	while i<2:   #这里循环两次累加全局变量,目的是增加出错的概率

        	temp=temp+1  #在子线程中实现对全局变量加1

        	print self.name+'--temp=='+str(temp)
        	i=i+1

	if __name__=='__main__':


		t1=myThread('线程1')
		t2=myThread('线程2')
		t3=myThread('线程3')

		#创建三个线程去执行访问
		t1.start()
		t2.start()
		t3.start()

Результаты выполнения (поскольку программа работает очень быстро, вы можете несколько раз выполнить следующие результаты): Мы можем обнаружить, что поток 1 и поток 2 одновременно обращаются к переменным, что эквивалентно печати

线程名线程1
线程名线程2
线程名线程3
线程1--temp==1线程2--temp==2
线程1--temp==3

线程2--temp==4
线程3--temp==5
线程3--temp==6
2. Ситуация блокировки
import threading,time

lock=threading.Lock()   #全局的锁对象
temp=0    #我们要多线程访问的全局属性

class myThread (threading.Thread):   #创建一个自定义线程类mythread,继承Thread

	def __init__(self,name):
    	"""
    	重新init方法
    	:param name: 线程名
    	"""
    	super(myThread, self).__init__(name=name)
    	# self.lock=lock

    	print '线程名'+name

	def run(self):
    	"""
    	重新run方法,这里面写我们的逻辑
    	:return:
    	"""
    	global temp,lock

    	if lock.acquire():  #这里线程进来访问变量的时候,锁定变量
        	i = 0
        	while i < 2:  # 这里循环两次累加全局变量,目的是增加出错的概率

            	temp = temp + 1  # 在子线程中实现对全局变量加1

            	print self.name + '--temp==' + str(temp)
            	i = i + 1

        	lock.release()  #访问完毕,释放锁让另外的线程访问



if __name__=='__main__':


	t1=myThread('线程1')
	t2=myThread('线程2')
	t3=myThread('线程3')

	#创建三个线程去执行访问
	t1.start()
	t2.start()
	t3.start()

Результаты запуска (сколько бы раз не запускалось, одновременного доступа не будет):

线程名线程1
线程名线程2
线程名线程3
线程1--temp==1
线程1--temp==2
线程2--temp==3
线程2--temp==4
线程3--temp==5
线程3--temp==6

Синхронизация потоков используется во многих местах, таких как захват билетов и лотерейные розыгрыши, Нам необходимо заблокировать некоторые ресурсы, чтобы предотвратить непредсказуемые ситуации во время многопоточного доступа.

очередь потоков

Очередь в python использует модуль Queue, который обеспечивает синхронную безопасную последовательность пар, включая очередь очереди FIFO (первым пришел, первым обслужен), очередь LIFO (последний пришел — первый вышел) LifoQueue и приоритетную очередь PriorityQueue. Все эти очереди реализованы примитивы блокировки, которые можно использовать непосредственно в нескольких потоках. Очереди могут использоваться для связи между потоками

Общие методы в модуле Queue:

  • Queue.qsize() возвращает размер очереди
  • Queue.empty() возвращает True, если очередь пуста, иначе False
  • Queue.full() возвращает True, если очередь заполнена, иначе False
  • Queue.full соответствует размеру maxsize
  • Queue.get([block[ timeout]]) для получения очереди, время ожидания timeout
  • Queue.get_nowait() эквивалентно Queue.get(False)
  • Queue.put(item) запись в очередь, время ожидания тайм-аута
  • Queue.put_nowait(item) эквивалентен Queue.put(item, False)
  • Queue.task_done() После завершения задания функция Queue.task_done() отправляет в очередь сигнал о том, что задание выполнено
  • Queue.join() на самом деле означает ожидание, пока очередь не станет пустой, прежде чем делать что-либо еще

пример:
tags=['one','tow','three','four','five','six']

q=Queue.LifoQueue()   #先入先出队列
for t in tags:
q.put(t)   #将数组数据加入队列
for i in range(6):
	print q.get()    #取出操作可以放在不同的线程中,不会出现同步的问题

результат:

six
five
four
three
tow
one

Q&A

Многопоточность в этой главе здесь. Мы в основном описываем ее базовое использование. Мы можем разработать другие варианты использования в соответствии с нашей собственной логикой в ​​​​будущем процессе разработки.