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.1.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.1-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: threadtools-1.0.1.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.1.tar.gz
Algorithm Hash digest
SHA256 e89f209d50df4f69c6a64712c49bd5d1476c9498ce227e6a541922d26089ba5d
MD5 4368cac1b6f5a2cea31cae76f4fc9443
BLAKE2b-256 ba8e902671eca41321c6aa84f78bead796cc2b6096f6f0efbd9a372e34372576

See more details on using hashes here.

Provenance

The following attestation bundles were made for threadtools-1.0.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: threadtools-1.0.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0d33e73638225f1abfd0adb1b3c45c3fb756d7652dba12f5f04698f26a32b992
MD5 a8b7b46f799f5b23c738c89f2e46d054
BLAKE2b-256 b0dcfc2cacdb402da946449d1bcdd0aec2c964e5a311cef75ed7a05708576ff2

See more details on using hashes here.

Provenance

The following attestation bundles were made for threadtools-1.0.1-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