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

Uploaded Python 3

File details

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

File metadata

  • Download URL: threadtools-1.0.0.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.0.tar.gz
Algorithm Hash digest
SHA256 82bd950cbe46d2021cc7e96aeb29794560f23954b254105c5beefc8715025256
MD5 0f299c7a5cdc2f023bb7f73c7ec43fce
BLAKE2b-256 2fc746081f256c5876d4d2b63ebd06054472673f00a131f9cfdd3ca71f81f989

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: threadtools-1.0.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9c73cae4b5eacb625919b7adaf397cdc5de2c3ea6d4f6ec134fa5a9dc5bf1968
MD5 f08b9844aaa22490f5f3fb0672bf5e53
BLAKE2b-256 52ce4f860b1bb4e7ad799cdbc3249a7b4da8a594c9ce47caa85e4f9017dfac26

See more details on using hashes here.

Provenance

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