Как сделать чтобы поток блокировался после вызова функции?
Есть две функции - fun_1() и fun_2().
Нужно запускать fun_1 каждые 10 сек. Из нее вызывается функция fun_2.
После того как отработал поток fun_1, нужно запускать отдельно поток с функцией fun_2.
fun_2 должна вызываться например 20 раз.
Основная проблема, что мне нужно сделать так:
- запустить поток с
fun_1. - После запустить
fun_2, прошло10сек,fun_2не успела отработать20раз, но уже время работатьfun_1. - Мне нужно приостановить второй поток с
fun_2, запустить первый поток сfun_1, когда отработаетfun_1возобновить поток сfun_2.
Я попыталась сделать через lock theared. Но получается так, что пока полностью не отработает поток 20 раз с функцией fun_2, поток с fun_1 не запустится.
Пример кода
import threading
from threading import Lock
import time
lock = Lock()
def fun_1():
i=0
while True:
print('fun1')
fun_2()
print('!!!!!')
time.sleep(10)
def fun_2():
global lock
with lock:
print('fun2')
if __name__=="__main__":
t1 = threading.Thread(target=fun_1)
t1.start()
copy = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
for item in copy:
t2 = threading.Thread(target=fun_2)
t2.start()
Ответы (2 шт):
Если добавить ожидание окончания t2, то можно обеспечить вклинивание исполнения t1 в цикле для t2.
Иначе получается, что вы сразу наплодили кучу потоков t2 (для всего списка), они всем скопом встали в очередь по lock, и func_1 вызвав func_2 после sleep встаёт только в конец этой очереди.
import threading
from threading import Lock
import time
lock = Lock()
def fun_1():
i=10
while i < 100:
print('fun1', i)
fun_2(i)
i += 10
print('!!!!!')
time.sleep(5)
def fun_2(x):
global lock
with lock:
print('fun2', x)
time.sleep(2)
if __name__=="__main__":
t1 = threading.Thread(target=fun_1)
t1.start()
for item in range(-1, -20, -1):
t2 = threading.Thread(target=lambda: fun_2(item))
t2.start()
t2.join()
fun1 10
fun2 10
!!!!!
fun2 -1
fun2 -2
fun2 -3
fun1 20
fun2 20
!!!!!
fun2 -4
fun2 -5
fun2 -6
fun1 30
fun2 30
!!!!!
fun2 -7
fun2 -8
fun2 -9
fun1 40
fun2 40
!!!!!
fun2 -10
fun2 -11
fun2 -12
fun1 50
fun2 50
...
Для работы в асинхронном режиме необходимо в задачах иметь место переключение контекста (в данном случае это asyncio.sleep()).
Обычно подобные места это операции чтения/записи; ввода/вывода; ожидания ответа на вебсокете и проч., они бывают как синхронные так и асинхронные
import asyncio
async def func1():
while True:
print(f'## func1 called')
await func2('### func2 from func1')
await asyncio.sleep(5)
async def func2(title=None):
print(f'{title or "func2"} called')
await asyncio.sleep(1)
async def async_main():
asyncio.create_task(func1())
for index in range(10):
await func2()
if __name__ == '__main__':
asyncio.run(async_main())
Временные интервалы уменьшены вдвое для удобства.
func2 called
## func1 called
### func2 from func1 called
func2 called
func2 called
func2 called
func2 called
func2 called
## func1 called
### func2 from func1 called
func2 called
func2 called
func2 called
func2 called
Process finished with exit code 0
Если в задачах нет мест для переключения контекста (все синхронное) то можно использовать .run_in_executor(self, executor, func, *args) в который передать синхронные функции.
import asyncio
import time
def func1():
while True:
print(f'## func1 called')
func2('### func2 from func1')
time.sleep(5)
def func2(title=None):
print(f'{title or "func2"} called')
time.sleep(1)
async def async_main():
asyncio.get_event_loop().run_in_executor(None, func1)
for index in range(10):
await asyncio.get_event_loop().run_in_executor(None, func2)
if __name__ == '__main__':
asyncio.run(async_main())
## func1 called
### func2 from func1 called
func2 called
func2 called
func2 called
func2 called
func2 called
func2 called
## func1 called
### func2 from func1 called
func2 called
func2 called
func2 called
func2 called
...