Skip to main content

Tools for synchronizing threads using better locks and signals.

Project description

threadtools

Support for signals and better locks in native Python.

Inspiration

PyQt lets you "emit" signals that have function callbacks tied to them. Why shouldn't we have that in native Python?

Typical Usage

Signals

import time
from threading import Thread

from threadtools import Signal, process_events


class ThreadedProcess:
    """Mimics a long-running process that updates its progress."""

    def __init__(self):
        self.somethingHappened = Signal[str]()
        self.countChanged = Signal[int]()
        self.finished = Signal()  # no typing implies no arguments to `emit()`

    def run(self):
        for i in range(1, 6):
            time.sleep(1)
            self.countChanged.emit(i)
            if i == 3:
                self.somethingHappened.emit("Something happened!")
        self.finished.emit()


threaded_process = ThreadedProcess()
thread = Thread(target=threaded_process.run)
# connect signals
threaded_process.countChanged.connect(print)
threaded_process.somethingHappened.connect(print)
threaded_process.finished.connect(lambda: print("Done!"))
# run the thread
thread.start()
# you must call `process_events()` to receive signals from other threads
# `emit()` was called from a different thread than `connect()`, so the callbacks are queued
while thread.is_alive():
    process_events()

# prints:
# 1
# 2
# 3
# Something happened!
# 4
# 5
# Done!

DataLock

from threading import Thread

from threadtools import DataLock

# DataLocks are generic; they support any type
LOCKED_INTEGER = DataLock(0)
LOCKED_STRING = DataLock("Hello, World!")


class DataAccessor:
    """Accesses and mutates data that is behind a lock."""

    def __init__(self, int_value: int):
        self.int_value = int_value

    def run(self):
        # using a context manager locks the lock and returns the stored data
        with LOCKED_INTEGER as locked_int:
            # reading data
            print(locked_int)

            # writing data
            
            # BAD!!
            # this does not change the value inside the lock
            # (Python references don't work that way)
            locked_int = self.int_value
            
            # good
            # DataLocks are reentrant; this will not cause a deadlock
            LOCKED_INTEGER.set(self.int_value)

            # you can also get the data inside the lock using `get()`
            print(LOCKED_INTEGER.get())
        # the lock is unlocked here, at the end of the context


first_accessor = DataAccessor(1)
first_thread = Thread(target=first_accessor.run)

second_accessor = DataAccessor(2)
second_thread = Thread(target=second_accessor.run)

first_thread.start()
second_thread.start()

first_thread.join()
second_thread.join()

# if `first_thread` is started first, prints:
# 0
# 1
# 1
# 2

Thread Safety

Signals are thread-safe as long as they are connected correctly. See the connect() method for more details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

threadtools-1.0.2.tar.gz (19.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

threadtools-1.0.2-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

Details for the file threadtools-1.0.2.tar.gz.

File metadata

  • Download URL: threadtools-1.0.2.tar.gz
  • Upload date:
  • Size: 19.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for threadtools-1.0.2.tar.gz
Algorithm Hash digest
SHA256 f9ea7bfaaf0228805cd3cf02a9a45b6baf8e7ffa2afef63b45459f04621c68ac
MD5 fc6923ea2bd088af72f7631b3a877744
BLAKE2b-256 a0002f83a1a23c016aa02afd3632682ea438994a5637c198ef47fa769d78bd4a

See more details on using hashes here.

Provenance

The following attestation bundles were made for threadtools-1.0.2.tar.gz:

Publisher: pypi.yml on drewlwhitney/threadtools

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file threadtools-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: threadtools-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 19.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for threadtools-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e6d914a4f9869f3fd672f0f49f30d405c3a23ad81530a8f11b3b3fbc11488cac
MD5 95c1591797c2b3d31489d3270827420a
BLAKE2b-256 5460f909d75661da84f2fbc9984d2d03d5ca45c709f704a0d94714a0564e5f91

See more details on using hashes here.

Provenance

The following attestation bundles were made for threadtools-1.0.2-py3-none-any.whl:

Publisher: pypi.yml on drewlwhitney/threadtools

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page