{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Threading-Beispiel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Aktualisieren und Ausgeben eines Counters:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2026-05-21T21:30:47.254076Z", "iopub.status.busy": "2026-05-21T21:30:47.253562Z", "iopub.status.idle": "2026-05-21T21:30:47.258251Z", "shell.execute_reply": "2026-05-21T21:30:47.257717Z", "shell.execute_reply.started": "2026-05-21T21:30:47.254050Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting up\n", "The count is 1\n", "The count is 2\n", "The count is 3\n", "The count is 4\n", "The count is 5\n", "The count is 6\n", "The count is 7\n", "The count is 8\n", "The count is 9\n", "The count is 10\n", "Finishing up\n" ] } ], "source": [ "counter = 0\n", "\n", "print(\"Starting up\")\n", "for _i in range(10):\n", " counter += 1\n", " print(f\"The count is {counter}\")\n", "print(\"Finishing up\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Beginnt mit Code, der klar und einfach ist und von oben nach unten ausgeführt wird. Er ist einfach zu entwickeln und inkrementell zu testen.\n", "\n", "
join() verbunden werden.\n",
"\n",
"4. Ihr könnt nicht darauf warten, dass Daemon-Threads abgeschlossen werden (sie sind Endlosschleifen); stattdessen solltet ihr join() auf der Queue selbst ausführen, so dass die Aufgaben erst zusammengefügt werden wenn alle Aufgaben in der Queue erledigt sind.\n",
"\n",
"5. Ihr könnt globale Variablen verwenden um zwischen Funktionen zu kommunizieren, allerdings nur innerhalb eines single-threaded Programm. In einem multi-thread-Programm könnt ihr globale Variablen jedoch nicht verwenden da sie mutable sind. Dann ist die bessere Lösung threading.local(), da sie in einem Thread zwar global ist, jedoch nicht darüber hinaus.\n",
"\n",
"6. Versucht niemals, einen Thread von außen zu beenden: ihr wisst nie, ob dieser Thread ein Lock einthält. Daher bietet Python auch keinen direkten Mechanismus zum beenden von Threads. Falls ihr dies jedoch mit ctypes versucht, ist dies ein Rezept für Deadlocks.\n",
"\n",
"Wenn wir nun diese Regeln anwenden, sieht unser Code so aus:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"execution": {
"iopub.execute_input": "2026-05-21T21:30:52.196330Z",
"iopub.status.busy": "2026-05-21T21:30:52.195936Z",
"iopub.status.idle": "2026-05-21T21:30:52.205935Z",
"shell.execute_reply": "2026-05-21T21:30:52.205496Z",
"shell.execute_reply.started": "2026-05-21T21:30:52.196291Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting up\n",
"The count is 1\n",
"The count is 2\n",
"The count is 3\n",
"The count is 4\n",
"The count is 5\n",
"The count is 6\n",
"The count is 7\n",
"The count is 8\n",
"The count is 9\n",
"The count is 10\n",
"Finishing up\n"
]
}
],
"source": [
"import queue\n",
"import threading\n",
"\n",
"\n",
"counter = 0\n",
"\n",
"counter_queue = queue.Queue()\n",
"\n",
"\n",
"def counter_manager():\n",
" \"\"\"I have EXCLUSIVE rights to update the counter variable\"\"\"\n",
" global counter\n",
"\n",
" while True:\n",
" increment = counter_queue.get()\n",
" counter += increment\n",
" print_queue.put(\n",
" [\n",
" f\"The count is {counter}\",\n",
" ],\n",
" )\n",
" counter_queue.task_done()\n",
"\n",
"\n",
"t = threading.Thread(target=counter_manager)\n",
"t.daemon = True\n",
"t.start()\n",
"del t\n",
"\n",
"print_queue = queue.Queue()\n",
"\n",
"\n",
"def print_manager():\n",
" while True:\n",
" job = print_queue.get()\n",
" for line in job:\n",
" print(line)\n",
" print_queue.task_done()\n",
"\n",
"\n",
"t = threading.Thread(target=print_manager)\n",
"t.daemon = True\n",
"t.start()\n",
"del t\n",
"\n",
"\n",
"def worker():\n",
" \"\"\"My job is to increment the counter and print the current count\"\"\"\n",
" counter_queue.put(1)\n",
"\n",
"\n",
"print_queue.put([\"Starting up\"])\n",
"worker_threads = []\n",
"for _i in range(10):\n",
" t = threading.Thread(target=worker)\n",
" worker_threads.append(t)\n",
" t.start()\n",
"for t in worker_threads:\n",
" t.join()\n",
"\n",
"counter_queue.join()\n",
"print_queue.put([\"Finishing up\"])\n",
"print_queue.join()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Sorgfältiges Threading mit Locks\n",
"\n",
"Wenn wir Threading mit Locks statt mit Queues machen, wirkt der Code noch aufgeräumter:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"execution": {
"iopub.execute_input": "2026-05-21T21:30:52.208159Z",
"iopub.status.busy": "2026-05-21T21:30:52.207967Z",
"iopub.status.idle": "2026-05-21T21:30:52.213835Z",
"shell.execute_reply": "2026-05-21T21:30:52.213436Z",
"shell.execute_reply.started": "2026-05-21T21:30:52.208143Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting up\n",
"The count is 1\n",
"The count is 2\n",
"The count is 3\n",
"The count is 4\n",
"The count is 5\n",
"The count is 6\n",
"The count is 7\n",
"The count is 8\n",
"The count is 9\n",
"The count is 10\n",
"Finishing up\n"
]
}
],
"source": [
"import threading\n",
"\n",
"\n",
"counter_lock = threading.Lock()\n",
"printer_lock = threading.Lock()\n",
"\n",
"counter = 0\n",
"\n",
"\n",
"def worker():\n",
" global counter\n",
" with counter_lock:\n",
" counter += 1\n",
" with printer_lock:\n",
" print(f\"The count is {counter}\")\n",
"\n",
"\n",
"with printer_lock:\n",
" print(\"Starting up\")\n",
"\n",
"worker_threads = []\n",
"for _i in range(10):\n",
" t = threading.Thread(target=worker)\n",
" worker_threads.append(t)\n",
" t.start()\n",
"for t in worker_threads:\n",
" t.join()\n",
"\n",
"with printer_lock:\n",
" print(\"Finishing up\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Schließlich noch einige Hinweise zu Locks:\n",
"\n",
"1. Locks sind nur sog. *Flags*, sie verhindern nicht wirklich zuverlässig.\n",
"2. Im Allgemeinen sollten Locks als primitives Hilfsmittel betrachtet werden, das in nicht-trivialen Beispielen schwierig zu verstehen ist. Bei komplexeren Anwendungen sollten also besser atomare Message Queues verwendet werden.\n",
"3. Je mehr Locks gleichzeitig gesetzt sind, desto geringer werden die Vorteile gleichzeitiger Verarbeitung."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.13 Kernel",
"language": "python",
"name": "python313"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.0"
},
"latex_envs": {
"LaTeX_envs_menu_present": true,
"autoclose": false,
"autocomplete": true,
"bibliofile": "biblio.bib",
"cite_by": "apalike",
"current_citInitial": 1,
"eqLabelWithNumbers": true,
"eqNumInitial": 1,
"hotkeys": {
"equation": "Ctrl-E",
"itemize": "Ctrl-I"
},
"labels_anchors": false,
"latex_user_defs": false,
"report_style_numbering": false,
"user_envs_cfg": false
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 2,
"version_minor": 0
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}