Ответы пользователя по тегу Многопоточность
  • Как принудительно завершить дополнительный поток (thread)?

    Vindicar
    @Vindicar
    RTFM!
    Просто прибить поток технически можно, но на практике чревато - нет способа контроля за тем, когда поток прибьётся, так что если это произойдёт в неподходящий момент, внутренняя машинерия питона может сломаться.

    Лучше проектируй свой счётчик времени, что тут скажешь. Хотя я бы лучше само воспроизведение звука вынес бы в отдельный поток, а главный поток пускай занимается трекингом.

    Пример, как можно сделать поток с ожиданием И быстрым прибитием.
    def long_worker_thread(event: threading.Event):
        ...  # тут начальная подготовка. имей ввиду, что цикл начнётся с ожидания
        while not event.wait(1.0):  # ждём пока пройдёт заданное время - или пока event не будет взведено
            ...  # тут работу работаем - но не слишком долго, чтобы проверки event.wait() делались регулярно!
        ...  # завершение. Произойдёт, если был сделан break, или если event было взведено
    
    stop_worker = threading.Event()
    thread = threading.Thread(target=long_worker_thread, args=(stop_worker,))
    thread.start()
    ...  # что-то делаем пока поток крутится
    stop_worker.set()  # ожидание в потоке прервётся немедленно, не дожидаясь конца интервала
    thread.join()  # поэтому можно спокойно дождаться, пока поток не закончит работу - это будет быстро
    Ответ написан
  • Как реализовать выход из программы или закрытие cmd консоли при вводе 'stop'?

    Vindicar
    @Vindicar
    RTFM!
    Грубое решение: настрой таймаут чтения, и при таймауте проверяй логическую переменную, которая хранит признак "пора остановиться". Тогда если ты эту переменную взведёшь, твои потоки-клиенты отвалятся, пусть и не сразу.
    Ответ написан
    Комментировать
  • Есть код на питоне как можно ускорить его мультипроцессность не работает?

    Vindicar
    @Vindicar
    RTFM!
    p = multiprocessing.Process(target=gen,args=(lst,))
    p.start()

    Ты запускаешь ровно один процесс, а потом стоишь и ждёшь (p.join()), когда он завершится. Это мало чем отличается от просто вызова gen() в твоём коде, безо всякого мультипроцессинга.
    Кроме того, у тебя функция gen() делает всю работу, а должна обрабатывать только один элемент. Ты думаешь Питон волшебным образом поймёт, что вот именно этот цикл надо распараллелить?

    Используй пул процессов multiprocessing.Pool, он принимает функцию и последовательность входных значений, а потом скармливает эти значения по одному в копии указанной функции в нескольких процессах, и собирает из них ответы.

    Пример есть в документации, если ты туда заглядывал. В примере показано несколько вариантов использования пула процессов.
    Я бы на твоём месте использовал или imap_unordered(), или imap(). Разница в том, что первый может не сохранять порядок - т.е. результаты тебе будут приходить не в том же порядке, в каком приходили данные во входной последовательности. Второй его сохраняет, но работает чуть медленнее (он ждёт, пока правильный порядок не получится).
    Ответ написан
    6 комментариев
  • Для чего нужен lock в python? Как работает данный пример кода?

    Vindicar
    @Vindicar
    RTFM!
    Это объясняется тем, что в базовом питоне потоки не вполне честные - они конкурируют за global interpreter lock, так что код выполняется всё равно поочерёдно. Так что многопоточность в питоне полезна с точки зрения распараллеливания, но не ускорения. ЕМНИП, есть реализации питона, в которых нет этой GIL problem.
    Но нужно иметь ввиду, что этот GIL блокирует только элементарные операции (как в твоём примере), тогда как явное использование lock может накрывать целые блоки кода, состоящие из нескольких операций с защищаемым ресурсом.

    Вот тебе пример:
    import threading
    import time
    
    class Data:
        def __init__(self):
            self.x: int = 0
            self.y: int = 0
    
    
    do_sleep = False
    run = True
    
    
    def reader(d: Data):
        while run:
            x, y = d.x, d.y
            # по идее это условие не должно выполниться никогда
            if (x != 0) != (y != 0):  
                print(f'Got x={x} and y={y}')
            else:
                print(f'OK {x}', end='\x08\x08\x08\x08')
    
    
    def writer(d: Data):
        while run:
            if d.x == 0:
                d.x = 1
                if do_sleep: pass
                d.y = 1
            else:
                d.x = 0
                if do_sleep: pass
                d.y = 0
    
    
    do_sleep = False
    instance = Data()
    reader_thread = threading.Thread(target=reader, args=(instance,), daemon=True)
    writer_thread = threading.Thread(target=writer, args=(instance,), daemon=True)
    reader_thread.start()
    writer_thread.start()
    try:
        input()
    finally:
        run = False
        reader_thread.join()
        writer_thread.join()


    На моей машине, если if do_sleep: pass закомментировать, то в консоли высвечивается только OK - иными словами, присваивание двух полей выполняется достаточно быстро, чтобы поток не успел переключиться в промежутке. Как следствие, reader() всегда видит либо x=0 y=0, либо x=1 y=1.
    Но если if do_sleep: pass оставить, то выполнение тела цикла замедляется достаточно, чтобы поток успел переключиться - и, как следствие, reader() начинает видеть структуру данных Data в неконсистентном состоянии, когда x=0 y=1 или когда x=1 y=0.
    И вот чтобы не гадать "успеет - не успеет", нужно в таких случаях защищать связные серии обращений к структуре с помощью мьютекса, ну или в питоновских терминах - Lock.
    Ответ написан
    Комментировать
  • Как реализовать многопоточность в Си?

    Vindicar
    @Vindicar
    RTFM!
    Многопоточность для какой платформы?
    Для unix - см. ответ serhiops.
    Для windows - читай про функцию api CreateThread().
    А на микроконтроллерных платформах свои заморочки, я с ними не знаком.
    Ответ написан
    Комментировать
  • Как из main.py запустить несколько других файлов .py, чтобы они работали параллельно?

    Vindicar
    @Vindicar
    RTFM!
    Другие идеи есть в документации, просто её надо читать.

    subprocess.Popen() БЕЗ использования wait() или communicate().
    Ответ написан
  • Можно ли узнать возврат функции при использовании многопоточности Thread на Python?

    Vindicar
    @Vindicar
    RTFM!
    Лучше используй долгоживущие потоки и пару queue.Queue. Из первой потоки в цикле извлекают задания и обрабатывают их, во вторую складывают ответы. Главный поток - наоборот, кладёт задания в первую очередь и извлекает ответы из второй.

    См. также: паттерн "поставщик-потребитель".
    Ответ написан
    Комментировать
  • Как запускать Process в скриптах которые импорируются?

    Vindicar
    @Vindicar
    RTFM!
    AttributeError: Can't pickle local object 'start.<locals>.f1'

    Вынеси функцию, которую загоняешь в процесс, на верхний уровень модуля. Не делай её локальной в другой функции.
    Ответ написан
    Комментировать
  • Как поделиться переменными между потоками из разных модулей?

    Vindicar
    @Vindicar
    RTFM!
    Для начала скажи, какую задачу ты пытаешься решить.
    Для случая с одноразовым потоком-воркером, тебе не нужна глобальная переменная. Достаточно класса.
    import threading
    
    class MyWorkerThread(threading.Thread):
        def __init__(self, arg1: float, arg2: float): # передаём потоку входные данные 
            # поток не должен их менять!
            super().__init__()
            self.arg1 = arg1
            self.arg2 = arg2
            self.result: t.Optional[float] = None
        def run(self):
            time.sleep(10) # имитируем длительную работу
            self.result = self.arg1 + self.arg2

    И тогда ты можешь его использовать так:
    worker = MyWorkerThread(42, 69)
    worker.start()
    while True: 
        if worker.is_alive():  # проверяем, жив ли поток
            # делаешь ещё что-то, пока поток работает
            print('Still working...')
            time.sleep(0.5)
        else:
            # поток завершился, даём знать пользователю.
            print(f'Done! Result is {worker.result}!')
            break # выходим из цикла

    Если же тебе нужно просто дождаться конца потока, ничего не делая в процессе, можно сделать просто worker.join()

    Сложности начинаются, когда тебе нужно взаимодействовать с длительным потоком. Усложним пример:
    import threading, queue
    
    class MyWorkerThread(threading.Thread):
        def __init__(self, arg1: float, arg2: float): # передаём потоку входные данные 
            # поток не должен их менять!
            super().__init__()
            self.arg1 = arg1
            self.arg2 = arg2
            self.result: t.Optional[float] = None
            self.progress = queue.Queue()
        def run(self):
            for i in range(10):
                time.sleep(1) # имитируем длительную работу
                self.progress.put(i/10) # сообщаем о прогрессе
            self.result = self.arg1 + self.arg2
            self.progress.put(1.00)

    Тогда код использования изменится следующим образом:
    worker = MyWorkerThread(42, 69)
    worker.start()
    while True: 
        if worker.is_alive():  # проверяем, жив ли поток
            # делаешь ещё что-то, пока поток работает
            try:
                progress = worker.progress.get(block=True, timeout=0.5)
            except queue.Empty: # поток ничего не сообщил
                print('Still working...')
            else:
                print(f'Still working... {progress:.0%}')
                worker.progress.task_done() # один вызов task_done() на один успешный вызов get()!
        else:
            # поток завершился, даём знать пользователю.
            print(f'Done! Result is {worker.result}!')
            break # выходим из цикла

    Если тебе нужно передать потоку новые задания, то можешь использовать ещё одну queue.
    Ответ написан
    Комментировать
  • Multiprocessing.Process OSError: [WinError 87] Параметр задан неверно, что делать?

    Vindicar
    @Vindicar
    RTFM!
    Ну так тебе же говорят:
    _pickle.PicklingError: Can't pickle <function <lambda> at 0x00000122F23CEB00>: attribute lookup <lambda> on __main__ failed

    Для передачи данных в соседний процесс они сериализуются через модуль pickle. А сериализовываться так может далеко не всё.
    Если тебе нужно скинуть в соседний процесс код... это проблема. Может, получится скинуть словарь с локальными переменными, строку с кодом, и вычислить эту строку через eval() уже "на месте".
    Альтернативно, просто замени лямбду на нормальную функцию, и попробуй передать либо её, либо имя этой функции, чтобы дочерний процесс сам её взял из globals() или еще откуда.
    Ответ написан
    Комментировать
  • Как запустить shell-комманду в новом процессе без блокировки потока?

    Vindicar
    @Vindicar
    RTFM!
    communicate() как раз и ждёт. Вместо этого ты можешь попробовать просто читать из proc.stdout.
    С асинхронностью сложнее, но ты можешь попробовать запустить функцию в потоке через loop.run_in_executor(). Это позволит завернуть поток в асинхронную задачу. Так как поток будет висеть в ожидании ввода-вывода, он не должен сильно влиять на GIL и мешать другим потокам.
    Ответ написан
    2 комментария
  • Реализация многопоточности в vk bot?

    Vindicar
    @Vindicar
    RTFM!
    Ну для начала, что должен делать этот новый поток?
    Оформи его действия отдельной функцией, и её и задавай.
    Другой вопрос, ты уверен что тебе нужны потоки для этого? И уверен ли ты, что с vk-api можно работать из нескольких потоков?
    Ответ написан
  • Как запустить поток через время?

    Vindicar
    @Vindicar
    RTFM!
    Запустить поток сразу, но в потоке первым делом уснуть на заданное время?
    Объект timedelta имеет метод totalseconds(), который даст длительность интервала в секундах, как раз как это требуется time.sleep().
    Для небольших интервалов (в пределах минут) сойдёт.
    Ответ написан
  • Как из дочернего процесса вытащить данные?

    Vindicar
    @Vindicar
    RTFM!
    Попробуй использовать Pool.map().
    Пример его использования буквально в самом начале официальной документации.
    Ответ написан
    2 комментария
  • Добавление аргументов к функции с threading?

    Vindicar
    @Vindicar
    RTFM!
    1. На момент вызова конструктора Thread переменная conn ещё не существует. Тебе нужно создавать поток тогда, когда она уже получила значение.
    2. Не вызывай метод run(). Он будет вызван сам, в отдельном потоке. Для запуска этого потока нужно вызвать start(). И имей ввиду, что у тебя send() отрабатывает однажды, и останавливается.

    Я бы посоветовал попрактиковаться в работе с потоками, для начала.
    Ответ написан
    Комментировать
  • Почему - то когда работаю с классом не через главный поток, переменные в классе не изменяются, почему?

    Vindicar
    @Vindicar
    RTFM!
    Ну второе как раз объяснимо, ты запускаешь work() для одного и того же класса.
    Пока первая копия work() спит в time.sleep(1), начинает выполняться вторая, так как всё происходит в отдельных потоках. Вторая видит isworking = 1, и останавливается.
    Ответ написан
    2 комментария
  • Как запустить одновременно два потока Python?

    Vindicar
    @Vindicar
    RTFM!
    В CPython есть проблема - в один момент времени исполняется всегда один поток, даже если ядер у проца несколько. Так что "одновременно" на питоне запустить два потока проблематично. Они будут чередоваться, исполняясь по кусочкам. Исключение - если поток ждёт завершения операции ввода-вывода, или чего-то подобного, т.е. не исполняет непосредственно код на питоне. Тогда он не блокирует другие потоки.

    При этом нет никаких гарантий насчёт порядка их исполнения. Когда ОС решит переключиться с одного потока на другой - ты не контролируешь. У меня, например, твой первый код тоже выводит "hellohello".

    Так что да, твой код выполняется настолько "одновременно", насколько это возможно.
    Ответ написан
  • Как использовать asyncio внутри Thread Python?

    Vindicar
    @Vindicar
    RTFM!
    Во-первых, несколько асинхронных задач прекрасно уживаются друг с другом. На кой тебе вообще нужны потоки для этого? Чем не устроил loop.create_task(), или asyncio.gather()?

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

    В-третьих, технически ты можешь создавать свой цикл реактора (asyncio loop) в каждом потоке. Почитай доки на asyncio как это делается. Но нужно помнить, что async-объекты из разных реакторов не дружат друг с другом. Так что подчеркиваю красным: не надо это делать. Лучше сначала попробуй по-иному подойти к проблеме.
    Ответ написан
    4 комментария
  • RuntimeError: There is no current event loop in thread 'Thread-2'. Что делать?

    Vindicar
    @Vindicar
    RTFM!
    Мешать многопоточность с асинхронностью - плохая идея. Зачем тебе это потребовалось?
    Если нужно выполнить длительную синхронную задачу в отдельном потоке, используй loop.run_in_executor() - он позволяет аккуратно представить выполнение потока как обычную асинхронную задачу.
    Ответ написан
    Комментировать
  • Как результат каждого процесса записать в отдельный соответствующий файл (Multiprocessing, Python)?

    Vindicar
    @Vindicar
    RTFM!
    for i,item in enumerate(data_group):
        file_name = f"{i}.txt"
        #ну и далее по тексту

    Как-то так? Я серьёзно не вижу в чём затруднение.
    Ответ написан
    Комментировать