I somehow wrote this code that allows only one writer at a time and there can't be readers while the writer is working. It works, but I don't know why, I understood that for wait_for() to wake up it was necessary to call notify() but I didn't and still works.
Thanks!
import threading
import time
condition = threading.Condition()
def test():
return True
def reader(data):
with condition:
condition.wait_for(test)
print('read')
def writer(data):
with condition:
time.sleep(5)
print('write')
def runThread(data):
if data["w"]: #writer
writer(data)
elif data["r"]: #reader
reader(data)
def run():
#something happens
threading.Thread(target=runThread, daemon=True, ).start()
Related
I want to write good tests to make sure my concurrent data structure works. But the tests are passing even on a class that is obviously not thread-safe.
class NotThreadSafe:
def __init__(self):
self.set1 = set()
self.set2 = set()
def add_to_sets(self, item):
self._add_to_set1(item)
self._add_to_set2(item)
def _add_to_set1(self, item):
self.set1.add(item)
def _add_to_set2(self, item):
self.set2.add(item)
def are_sets_equal_length(self):
return len(self.set1) == len(self.set2)
My tests have a reader thread and a writer thread running concurrently. The writer thread calls add_to_sets and the reader thread calls are_sets_equal_length.
But the reader thread always observes are_sets_equal_length to be True, even though the writer thread should theoretically cause inequalities.
How can I add some time delay on add_to_set2 so that it forces the race condition to surface?
The test:
import threading
import time
def writer_fn(nts: NotThreadSafe):
for i in range(1000):
nts.add_to_sets(i)
def reader_fn(nts: NotThreadSafe, stop: list, results: list):
while not len(stop):
if not nts.are_sets_equal_length():
results.append(False)
return
results.append(True)
def test_nts():
nts = NotThreadSafe()
stop = []
results = []
reader = threading.Thread(target=reader_fn, args=[nts, stop, results])
writer = threading.Thread(target=writer_fn, args=[nts])
reader.start()
writer.start()
writer.join()
stop.append(True)
reader.join()
assert not results[0]
Step 1: write a wrapper that creates a new function containing a time delay.
def slow_wrapper(method):
"""Adds a tiny delay to a method. Good for triggering race conditions that would otherwise be very rare."""
def wrapped_method(*args):
time.sleep(0.001)
return method(*args)
return wrapped_method
Step 2: In the test function, after creating the object, change add_to_set2 into a slow version:
nts = NotThreadSafe()
# change _add_to_set2 into a time-delayed version
nts._add_to_set2 = slow_wrapper(nts._add_to_set2)
Step 3: Run the tests. The failure should be triggered properly.
I have a class to start a thread for a while loop. I tried to scheduling the thread class to start within a certain time but it doesn't work:
def test():
if __name__ == "__main__":
main()
schedule.every().day.at("17:25:50").do(test)
The function does not do anything even the time reached "17:25:50"
My full code:
import discord
import random
import time
import asyncio
import schedule
from facebook_scraper import get_posts, _scraper, exceptions
from discord.ext import commands, tasks
import threading
import time
import re
class LEDManager(threading.Thread):
def __init__(self, id_manager):
threading.Thread.__init__(self)
self.id_manager = int(id_manager)
def run(self):
while True:
try:
wanted = "Pecahan setiap negeri (Kumulatif):" # wanted post
for post in get_posts("myhealthkkm", pages=5):
if post.get("post_text") is not None and wanted in post.get("post_text"):
# print("Found", t)
listposts.append(post.get("post_text"))
# append until 3 page finish then go here
time.sleep(1)
print(listposts)
global listView
if listposts != 0:
listView = listposts.copy()
print(listView)
listposts.clear()
except exceptions.TemporarilyBanned:
print("Temporarily banned, sleeping for 10m")
time.sleep(600)
def main():
thread_id = ("0")
led_index = 0
thread_list = list()
for objs in thread_id:
thread = LEDManager(led_index)
thread_list.append(thread)
led_index += 1
for thread in thread_list:
thread.start()
time.sleep(1)
def test():
if __name__ == "__main__":
main()
schedule.every().day.at("17:25:50").do(test)
You forgot to add these lines:
while True:
schedule.run_pending()
time.sleep(1)
You should add them at the end of the file, so the system will keep checking forever, if "the job" needs to be done (if the hour is "17:25:50").
And here is the full documentation to see how to use the schedule module:
https://schedule.readthedocs.io/en/stable/
Note: the suggested answer doesn't work as I call the function every x seconds.
In python I have:
def inline_paid_generator(user_id, password):
login_thread = threading.Thread(target=paid_generator, name="Register", args={user_id,password})
login_thread.start()
How outside that function (from where I called it) how can I know if the thread is alive or not?
You can set the thread as a global variable and only start it in the function, as following:
import time
import threading
def wait():
time.sleep(5)
login_thread = threading.Thread(target=wait)
def start_inline_paid_generator():
login_thread.start()
print(login_thread.is_alive()) # False
start_inline_paid_generator()
print(login_thread.is_alive()) # True
Then you can check anywhere in your program with login_thread.is_alive()
Return the created thread from the function and check if it's alive:
def inline_paid_generator(user_id, password):
login_thread = threading.Thread(target=paid_generator, name="Register", args={user_id,password})
login_thread.start()
return login_thread
t = inline_paid_generator(...)
...
if not t.is_alive():
t = inline_paid_generator(...)
Before you tell me to look at this link, please know I have already tried that approach and I can't understand it. Maybe I'm using it wrong, but it didn't work for me.
import threading
import asyncio
from collections import deque
import queue
f = queue.Queue()
nq = deque()
loop = asyncio.get_event_loop()
async def t():
return 5
def func():
f.put(t())
func()
class T(threading.Thread):
def func(self, value):
fut = asyncio.run_coroutine_threadsafe(value, loop)
nq.append(fut)
print(nq) # deque([<Future at 0x3afa0a0 state=pending>])
def run(self):
while True:
try:
x = f.get(0.2)
print(x) # <coroutine object t at 0x03AF5068>
except:
continue
self.func(x)
T().start()
while True:
try:
val = nq.pop()
break
except IndexError:
continue
print(val) # <Future at 0x3afa0a0 state=pending>
# I'm not even bothering to call `val.result()` because I know the future doesn't have a result set.
I am not sure as to why this future never resolves. Essentially, what I am trying to do is run a coroutine in a thread and return the result in the main thread with the help of a queue/container system.
I can think of two ways to break out of a loop in a Python thread, minimal examples below:
1 - Use a sentinel value
from threading import Thread, Event
from time import sleep
class SimpleClass():
def do_something(self):
while self.sentinel:
sleep(1)
print('loop completed')
def start_thread(self):
self.sentinel = True
self.th = Thread(target=self.do_something)
self.th.start()
def stop_thread(self):
self.sentinel = False
self.th.join()
simpleinstance = SimpleClass()
simpleinstance.start_thread()
sleep(5)
simpleinstance.stop_thread()
2 - Use an Event
from threading import Thread, Event
from time import sleep
class SimpleThread(Thread):
def __init__(self):
super(SimpleThread, self).__init__()
self.stoprequest = Event()
def run(self):
while not self.stoprequest.isSet():
sleep(1)
print('loop completed')
def join(self, timeout=None):
self.stoprequest.set()
super(SimpleThread, self).join(timeout)
simpleinstance = SimpleThread()
simpleinstance.start()
sleep(5)
simpleinstance.join()
In the Python documentation, it discusses events but not the simpler 'sentinel value' approach (which I see used in many threading answers on Stack Overflow).
Is there any disadvantage to using the sentinel value?
Specifically, could it cause errors (I have never had one but I imagine if you tried to change the value of the sentinel at exactly the same moment it was being read for the while loop then something could break (or maybe the CPython GIL would save me in this case). What is considered best (safest) practice?
If you look at the source of Event, you can see that the function you are using don't have any more value for you:
class Event:
def __init__(self):
self._cond = Condition(Lock())
self._flag = False
def is_set(self):
return self._flag
def set(self):
with self._cond:
self._flag = True
self._cond.notify_all() # No more-value, because you are not using Event.wait
So in your case Event is just a fancy wrapper for a sentinel value with no actually use, that will also slow down your operation time by a really tiny amount.
Events are only useful if you use their wait method.