threadlet


Namethreadlet JSON
Version 3.2.1 PyPI version JSON
download
home_pageNone
SummaryConvenient threading and improved ThreadPoolExecutor
upload_time2024-06-29 09:03:15
maintainerNone
docs_urlNone
authorNone
requires_python>=3.7
licenseNone
keywords thread pool threads
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # threadlet

[![PyPI - Version](https://img.shields.io/pypi/v/threadlet.svg)](https://pypi.org/project/threadlet)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/threadlet.svg)](https://pypi.org/project/threadlet)

More convenient threads and pools with futures.

```python
from threadlet import spawn, go


def plus2(n):
  return n + 2


# run function in thread
future = spawn(plus2, 1)
assert future.result() == 3

# run function in adaptive thread pool executor
future = go(plus2, 2)
assert future.result() == 4
```

* **spawn** is a helper which runs function in a separate thread and returns `Future`.
* **go** is a similar helper, but runs function in adaptive thread pool executor which is handled in background.
* **Task** is a wrapper for encapsulating a function, its arguments and `Future` object.
* **Worker** is a thread with a loop for executing incoming tasks.
* **SimpleThreadPoolExecutor** is a simple variant of `concurrent.futures.ThreadPoolExecutor` which spawns all the threads at the beginning.
* **ThreadPoolExecutor** is an adaptive variant of the `concurrent.futures.ThreadPoolExecutor` which automatically spawns and shutdowns threads depending on load.
One thread in the pool lives forever, new threads are spawned on `submit` call if there are no idle threads and die after some idle time(1 second by default).

-----

**Table of Contents**

- [Installation](#installation)
- [Usage](#usage)
- [Benchmarks](#benchmarks)
- [License](#license)

## Installation

```console
pip install threadlet
```

## Usage

```python
import threading
from threadlet import (
    spawn,
    go,
    Future,
    Task,
    Worker,
    SimpleThreadPoolExecutor,
    ThreadPoolExecutor,
)


def calc(x):
    return x * 2


# execute function in an adaptive thread pool executor
# which is going to be started automatically at first `go` call and shut down at application exit
future = go(calc, 2)
assert future.result() == 4
# is equivalent to:
with ThreadPoolExecutor() as tpe:
    future = tpe.submit(calc, 2)
    assert future.result() == 4

# execute function in a separate thread:
future = spawn(calc, 2)
assert future.result() == 4
# is equivalent to:
task = Task(Future(), calc, [2], {})
threading.Thread(target=task.run).start()
assert task.future.result() == 4

# spawns one thread(worker) to sequentially handle all submitted functions
with Worker() as w:
    f1 = w.submit(calc, 3)
    f2 = w.submit(calc, 4)
    assert f1.result() == 6
    assert f2.result() == 8

# spawns 4 threads(workers) to handle all tasks in parallel
with SimpleThreadPoolExecutor(4) as tpe:
    future = tpe.submit(calc, 5)
    assert future.result() == 10
```


## Benchmarks

* submit: submits 1 million futures.
* e2e[N] (end to end[N workers]): submits 1 million futures using N workers and consumes results in a separate thread.

```
concurrent.futures.thread.ThreadPoolExecutor submit: time=12.94s size=0.04mb, peak=43.61mb
                threadlet.ThreadPoolExecutor submit: time= 2.89s size=0.04mb, peak=20.35mb
          threadlet.SimpleThreadPoolExecutor submit: time= 2.72s size=0.04mb, peak=24.49mb

concurrent.futures.thread.ThreadPoolExecutor e2e[1]: time=15.80s size=0.04mb, peak=28.56mb
                threadlet.ThreadPoolExecutor e2e[1]: time= 4.32s size=0.02mb, peak=19.48mb
          threadlet.SimpleThreadPoolExecutor e2e[1]: time= 4.23s size=0.02mb, peak=26.45mb

concurrent.futures.thread.ThreadPoolExecutor e2e[2]: time=33.36s size=0.07mb, peak=32.00mb
                threadlet.ThreadPoolExecutor e2e[2]: time= 4.35s size=0.02mb, peak=35.83mb
          threadlet.SimpleThreadPoolExecutor e2e[2]: time= 4.18s size=0.02mb, peak=41.81mb

concurrent.futures.thread.ThreadPoolExecutor e2e[4]: time= 7.49s size=0.11mb, peak=42.97mb
                threadlet.ThreadPoolExecutor e2e[4]: time= 4.37s size=0.03mb, peak=28.21mb
          threadlet.SimpleThreadPoolExecutor e2e[4]: time= 4.30s size=0.02mb, peak=39.81mb

concurrent.futures.thread.ThreadPoolExecutor e2e[8]: time= 7.30s size=0.21mb, peak=41.04mb
                threadlet.ThreadPoolExecutor e2e[8]: time= 4.49s size=0.05mb, peak=29.20mb
          threadlet.SimpleThreadPoolExecutor e2e[8]: time= 4.18s size=0.03mb, peak=38.36mb
```

## License

`threadlet` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "threadlet",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": null,
    "keywords": "thread pool, threads",
    "author": null,
    "author_email": "Andrii Kuzmin <jack.cvr@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/11/87/b2415c4e1ac5a03021a7747eb8672db8cdbebfb19ae439210b7c464550ca/threadlet-3.2.1.tar.gz",
    "platform": null,
    "description": "# threadlet\n\n[![PyPI - Version](https://img.shields.io/pypi/v/threadlet.svg)](https://pypi.org/project/threadlet)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/threadlet.svg)](https://pypi.org/project/threadlet)\n\nMore convenient threads and pools with futures.\n\n```python\nfrom threadlet import spawn, go\n\n\ndef plus2(n):\n  return n + 2\n\n\n# run function in thread\nfuture = spawn(plus2, 1)\nassert future.result() == 3\n\n# run function in adaptive thread pool executor\nfuture = go(plus2, 2)\nassert future.result() == 4\n```\n\n* **spawn** is a helper which runs function in a separate thread and returns `Future`.\n* **go** is a similar helper, but runs function in adaptive thread pool executor which is handled in background.\n* **Task** is a wrapper for encapsulating a function, its arguments and `Future` object.\n* **Worker** is a thread with a loop for executing incoming tasks.\n* **SimpleThreadPoolExecutor** is a simple variant of `concurrent.futures.ThreadPoolExecutor` which spawns all the threads at the beginning.\n* **ThreadPoolExecutor** is an adaptive variant of the `concurrent.futures.ThreadPoolExecutor` which automatically spawns and shutdowns threads depending on load.\nOne thread in the pool lives forever, new threads are spawned on `submit` call if there are no idle threads and die after some idle time(1 second by default).\n\n-----\n\n**Table of Contents**\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [Benchmarks](#benchmarks)\n- [License](#license)\n\n## Installation\n\n```console\npip install threadlet\n```\n\n## Usage\n\n```python\nimport threading\nfrom threadlet import (\n    spawn,\n    go,\n    Future,\n    Task,\n    Worker,\n    SimpleThreadPoolExecutor,\n    ThreadPoolExecutor,\n)\n\n\ndef calc(x):\n    return x * 2\n\n\n# execute function in an adaptive thread pool executor\n# which is going to be started automatically at first `go` call and shut down at application exit\nfuture = go(calc, 2)\nassert future.result() == 4\n# is equivalent to:\nwith ThreadPoolExecutor() as tpe:\n    future = tpe.submit(calc, 2)\n    assert future.result() == 4\n\n# execute function in a separate thread:\nfuture = spawn(calc, 2)\nassert future.result() == 4\n# is equivalent to:\ntask = Task(Future(), calc, [2], {})\nthreading.Thread(target=task.run).start()\nassert task.future.result() == 4\n\n# spawns one thread(worker) to sequentially handle all submitted functions\nwith Worker() as w:\n    f1 = w.submit(calc, 3)\n    f2 = w.submit(calc, 4)\n    assert f1.result() == 6\n    assert f2.result() == 8\n\n# spawns 4 threads(workers) to handle all tasks in parallel\nwith SimpleThreadPoolExecutor(4) as tpe:\n    future = tpe.submit(calc, 5)\n    assert future.result() == 10\n```\n\n\n## Benchmarks\n\n* submit: submits 1 million futures.\n* e2e[N] (end to end[N workers]): submits 1 million futures using N workers and consumes results in a separate thread.\n\n```\nconcurrent.futures.thread.ThreadPoolExecutor submit: time=12.94s size=0.04mb, peak=43.61mb\n                threadlet.ThreadPoolExecutor submit: time= 2.89s size=0.04mb, peak=20.35mb\n          threadlet.SimpleThreadPoolExecutor submit: time= 2.72s size=0.04mb, peak=24.49mb\n\nconcurrent.futures.thread.ThreadPoolExecutor e2e[1]: time=15.80s size=0.04mb, peak=28.56mb\n                threadlet.ThreadPoolExecutor e2e[1]: time= 4.32s size=0.02mb, peak=19.48mb\n          threadlet.SimpleThreadPoolExecutor e2e[1]: time= 4.23s size=0.02mb, peak=26.45mb\n\nconcurrent.futures.thread.ThreadPoolExecutor e2e[2]: time=33.36s size=0.07mb, peak=32.00mb\n                threadlet.ThreadPoolExecutor e2e[2]: time= 4.35s size=0.02mb, peak=35.83mb\n          threadlet.SimpleThreadPoolExecutor e2e[2]: time= 4.18s size=0.02mb, peak=41.81mb\n\nconcurrent.futures.thread.ThreadPoolExecutor e2e[4]: time= 7.49s size=0.11mb, peak=42.97mb\n                threadlet.ThreadPoolExecutor e2e[4]: time= 4.37s size=0.03mb, peak=28.21mb\n          threadlet.SimpleThreadPoolExecutor e2e[4]: time= 4.30s size=0.02mb, peak=39.81mb\n\nconcurrent.futures.thread.ThreadPoolExecutor e2e[8]: time= 7.30s size=0.21mb, peak=41.04mb\n                threadlet.ThreadPoolExecutor e2e[8]: time= 4.49s size=0.05mb, peak=29.20mb\n          threadlet.SimpleThreadPoolExecutor e2e[8]: time= 4.18s size=0.03mb, peak=38.36mb\n```\n\n## License\n\n`threadlet` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Convenient threading and improved ThreadPoolExecutor",
    "version": "3.2.1",
    "project_urls": {
        "Documentation": "https://github.com/jackcvr/threadlet#readme",
        "Issues": "https://github.com/jackcvr/threadlet/issues",
        "Source": "https://github.com/jackcvr/threadlet"
    },
    "split_keywords": [
        "thread pool",
        " threads"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9b690f25e09d402bfb432574f1ace8c607032a9c35f01dc37823c6ef1a1702c1",
                "md5": "442b8f3038487e7d230a155ebdd0b46b",
                "sha256": "ede3bd855d215fefa4fac90b164439fcc40f699c614d50a26c5bd598ffdaaebc"
            },
            "downloads": -1,
            "filename": "threadlet-3.2.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "442b8f3038487e7d230a155ebdd0b46b",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 5630,
            "upload_time": "2024-06-29T09:03:13",
            "upload_time_iso_8601": "2024-06-29T09:03:13.711925Z",
            "url": "https://files.pythonhosted.org/packages/9b/69/0f25e09d402bfb432574f1ace8c607032a9c35f01dc37823c6ef1a1702c1/threadlet-3.2.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "1187b2415c4e1ac5a03021a7747eb8672db8cdbebfb19ae439210b7c464550ca",
                "md5": "d0fc02b896da3ad89ee1f0e963fe9e33",
                "sha256": "d40b4b6b3a6cf10b78c19a54d25e47ad3f7425ed2adc14593d98d0fd4834be56"
            },
            "downloads": -1,
            "filename": "threadlet-3.2.1.tar.gz",
            "has_sig": false,
            "md5_digest": "d0fc02b896da3ad89ee1f0e963fe9e33",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 8728,
            "upload_time": "2024-06-29T09:03:15",
            "upload_time_iso_8601": "2024-06-29T09:03:15.505505Z",
            "url": "https://files.pythonhosted.org/packages/11/87/b2415c4e1ac5a03021a7747eb8672db8cdbebfb19ae439210b7c464550ca/threadlet-3.2.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-06-29 09:03:15",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jackcvr",
    "github_project": "threadlet#readme",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "threadlet"
}
        
Elapsed time: 4.22794s