Как сделать чтобы поток блокировался после вызова функции?

Есть две функции - 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 шт):

Автор решения: GrAnd

Если добавить ожидание окончания 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
...
→ Ссылка
Автор решения: Tr1nks

Для работы в асинхронном режиме необходимо в задачах иметь место переключение контекста (в данном случае это 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
...
→ Ссылка