Wait for a While Without Blocking Main Thread

Wait for a while without blocking main thread

Thread.Sleep(500) will force the current thread to wait 500ms. It works, but it's not what you want if your entire application is running on one thread.

In that case, you'll want to use a Timer, like so:

using System.Timers;

void Main()
{
Timer t = new Timer();
t.Interval = 500; // In milliseconds
t.AutoReset = false; // Stops it from repeating
t.Elapsed += new ElapsedEventHandler(TimerElapsed);
t.Start();
}

void TimerElapsed(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Hello, world!");
}

You can set AutoReset to true (or not set it at all) if you want the timer to repeat itself.

Waiting for coroutine result without blocking main thread

Coroutines are designed to get rid of callbacks. You can use lifecycleScope in the Fragment class to launch a lifecycle-aware coroutine, it will look like the following:

MyFragment.kt:

fun updateButtonText() = lifecycleScope.launch {
button.text = handlerClass.getData()
}

HandlerClass.kt:

suspend fun getData() {
return mySuspendedNetworkcallMethod()
}

If you use MVVM approach you should consider to use ViewModel and it's viewModelScope extension to launch coroutines.

For LifecycleScope, use androidx.lifecycle:lifecycle-runtime-ktx:2.4.0 or higher.

For ViewModelScope, use androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0 or higher.

Start and wait for new thread without blocking UI thread

async void button1_Click(....)
{
label1.Text = "busy...";
await Task.Run(() => { RunOldCode(); });
label1.Text = "done."; // safe to use the UI
}

Note that async void should only be used for (simple) eventhandlers. It is problematic in most other situations.

Waiting for a result before continuing and without blocking UI

This is basically what you want. I'm violating a couple best-practices here, but just showing it's not that complicated. One thing to keep in mind is that the user can now click this button multiple times in a row. You might consider disabling it before processing. Or you can do a Monitor.TryEnter() to make sure it's not already running.

    private async void buttonProcess_Click(object sender, RoutedEventArgs e)
{
textBlockStatus.Text = "Processing...";
bool processed = await Task.Run(() => SlowRunningTask());
}

private bool SlowRunningTask()
{
Thread.Sleep(5000);
return true;
}

wait in separate thread without blocking main eventloop, async python

Threads and asyncio don't go together, except in specific circumstances (e.g. the implementation of run_in_executor). Instead of spawning new threads, spawn new coroutines.

For example:

import asyncio

async def dispatch_event(event, alert):
print(event)
if event == 'InstrInstallSucceeded':
# spawn a coroutine if you need something done in parallel
#asyncio.create_task(xxx())
await asyncio.sleep(1)
if event == 'InstrInstallFailed':
await asyncio.sleep(.5)

# alert the watcher(s) of the event that was dispatched
alert.last_event = event
alert.set()

async def keep_print(alert):
while True:
print(f'Beginning FLAG:: {alert.last_event}')
await alert.wait()
alert.clear()
print(f'End FLAG:: {alert.last_event}')

async def main():
alert = asyncio.Event()
alert.last_event = None
# spawn keep_print in the "background"
t = asyncio.create_task(keep_print(alert))
for i in ['InstrInstallSucceeded', 'ToolOn','ToolOn', 'ToolOn', 'InstrInstallFailed']:
await asyncio.sleep(1)
await dispatch_event(i, alert)
await asyncio.sleep(1)
t.cancel()

asyncio.run(main())

Wait for thread to finish without blocking the UI thread

There is a big difference between WinForms and Unity. In WinForms you have one thread for UI which could be blocked by a modal form. In Unity you have multiple objects with multiple methods where script execution order and some engine mechanisms decide how they should be executed in each frame.

However if you want to have a modal message box in Unity, you can simply block the execution of the Update or FixedUpdate of a specific script by adding a boolean check to it or by disabling the script. First way provides more options but second one is easier. However be aware that disabling a script stops everything in it except Invoke and Coroutine.

You can block user's interactions with underlying objects by putting a simple SpriteRenderer or Image over them. This mask can have zero transparency, should be full screen size and must have Raycast Target toggled on.

I would prefer a message box with a fullscreen mask behind it which has a simple black sprite with alpha = .1

public GameObject ModalMessageBox;//game object of message box with a mask

public void TestClick()
{
StartCoroutine(TestMethod);
ModalMessageBox.setActive(true);
}

IEnumerator TestMethod()
{
float time = 0;
while (time <= 20)
{
yield return new WaitForSeconds(.1f);
time++;
Debug.Log("Im doing heavy work");
}
ModalMessageBox.setActive(false);
}

void Update()
{
if(ModalMessageBox.activeSelf)
{
//handle message box
}
else
{
//handle normal update stuff
}
}

Note that all other scripts will run nevertheless. If you have to block the execution of other scripts as well, then you need to do it one by one.

Note:

Since disabling a script does not stop the coroutines it started, you might as well disable the script itself

public Script1 script1;
public Script2 script2;
public Script3 script3;

void BlockScripts(bool block)
{
//for singleton scripts:
Script1.Instance.enabled = !block;
Script2.Instance.enabled = !block;
Script3.Instance.enabled = !block;
//for referenced scripts:
script1.enabled = !block;
script2.enabled = !block;
script3.enabled = !block;

//self
enabled = !block;
}

public void TestClick()
{
StartCoroutine(TestMethod);
ModalMessageBox.setActive(true);

BlockScripts(true);
}

IEnumerator TestMethod()
{
float time = 0;
while (time <= 20)
{
yield return new WaitForSeconds(.1f);
time++;
Debug.Log("Im doing heavy work");
}

ModalMessageBox.setActive(false);

BlockScripts(false);
}

void Update()
{
}

where Script1,2,3 are singleton classes and script1,2,3 are references of scripts you want to block.

How to wait for thread to complete without blocking UI

I'm very surprised you haven't worked with any of these before but I would really recommend reading about threading in C# since it's fundamentally important to understand the intricacies and learning the language.

Below are three different ways you can achieve what you want:

1. Using reset events (further reading: https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx). If your C# version doesn't have the ManualResetEventSlim, replace it with ManualResetEvent and change Wait() with WaitOne()

class LockingWithResetEvents
{
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);

public void Test()
{
MethodUsingResetEvents();
}

private void MethodUsingResetEvents()
{
ThreadPool.QueueUserWorkItem(_ => DoSomethingLong());
ThreadPool.QueueUserWorkItem(_ => ShowMessageBox());
}

private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
_resetEvent.Set();
}

private void ShowMessageBox()
{
_resetEvent.WaitOne();
Console.WriteLine("Hello world.");
}
}

2) Using Task Parallel Library (TPL). Further reading: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

class LockingWithTPL
{
public void Test()
{
Task.Factory.StartNew(DoSomethingLong).ContinueWith(result => ShowMessageBox());
}

private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
}

private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}

3) Using Async/Await. Further reading: https://msdn.microsoft.com/en-us/library/hh191443.aspx

class LockingWithAwait
{
public void Test()
{
DoSomething();
}

private async void DoSomething()
{
await Task.Run(() => DoSomethingLong());
ShowMessageBox();
}

private async void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(10000);
}

private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}

Also good to know: Mutex (https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx), Semaphore (https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx), Lock (https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx), SemaphoreSlim (https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx), Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) and Interlocked (https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx).

How can I fire and forget a task without blocking main thread?

Your questions are so abstract that I'll try to give common answers to all of them.

How can I "fire and forget" a task without blocking main thread?

It depends on what you mean by saying forget.

  • If you are not planning to access that task after running, you can run it in a parallel process.
  • If the main application should be able to access a background task, then you should have an event-driven architecture. In that case, the things previously called tasks will be services or microservices.

I don't want to use any task queues (celery, rabbitmq, etc.) here because the tasks I'm thinking of are too small and fast to run. Just want to get them done as out of the way as possible. Would that be an async approach? Throwing them onto another process?

If it contains loops or other CPU-bound operations, then right to use a subprocess. If the task makes a request (async), reads files, logs to stdout, or other I/O bound operations, then it is right to use coroutines or threads.

Does it make sense to have a separate thread that handles background jobs? Like a simple job queue but very lightweight and does not require additional infrastructure?

We can't just use a thread as it can be blocked by another task that uses CPU-bound operations. Instead, we can run a background process and use pipes, queues, and events to communicate between processes. Unfortunately, we cannot provide complex objects between processes, but we can provide basic data structures to handle status changes of the tasks running in the background.

Regarding the Starlette and the BackgroundTask

Starlette is a lightweight ASGI framework/toolkit, which is ideal for building async web services in Python. (README description)

It is based on concurrency. So even this is not a generic solution for all kinds of tasks.
NOTE: Concurrency differs from parallelism.

I'm wondering if we can build something more generic where you can run background tasks in scripts or webservers alike, without sacrificing performance.

The above-mentioned solution suggests use a background process. Still, it will depend on the application design as you must do things (emit an event, add an indicator to the queue, etc.) that are needed for communication and synchronization of running processes (tasks). There is no generic tool for that, but there are situation-dependent solutions.

Situation 1 - The tasks are asynchronous functions

Suppose we have a request function that should call an API without blocking the work of other tasks. Also, we have a sleep function that should not block anything.

import asyncio
import aiohttp

async def request(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
try:
return await response.json()
except aiohttp.ContentTypeError:
return await response.read()

async def sleep(t):
await asyncio.sleep(t)

async def main():
background_task_1 = asyncio.create_task(request("https://google.com/"))
background_task_2 = asyncio.create_task(sleep(5))

... # here we can do even CPU-bound operations

result1 = await background_task_1

... # use the 'result1', etc.

await background_task_2

if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

In this situation, we use asyncio.create_task to run a coroutine concurrently (like in the background). Sure we could run it in a subprocess, but there is no reason for that as it would use more resources without improving the performance.

Situation 2 - The tasks are synchronous functions (I/O bound)

Unlike the first situation where the functions were already asynchronous, in this situation, those are synchronous but not CPU-bound (I/O bound). This gives an ability to run them in threads or make them asynchronous (using asyncio.to_thread) and run concurrently.

import time
import asyncio
import requests

def asynchronous(func):
"""
This decorator converts a synchronous function to an asynchronous

Usage:
@asynchronous
def sleep(t):
time.sleep(t)

async def main():
await sleep(5)
"""

async def wrapper(*args, **kwargs):
await asyncio.to_thread(func, *args, **kwargs)

return wrapper

@asynchronous
def request(url):
with requests.Session() as session:
response = session.get(url)
try:
return response.json()
except requests.JSONDecodeError:
return response.text

@asynchronous
def sleep(t):
time.sleep(t)


async def main():
background_task_1 = asyncio.create_task(request("https://google.com/"))
background_task_2 = asyncio.create_task(sleep(5))
...

Here we used a decorator to convert a synchronous (I/O bound) function to an asynchronous one and use them like in the first situation.

Situation 3 - The tasks are synchronous functions (CPU-bound)

To run CPU-bound tasks parallelly in the background we have to use multiprocessing. And for ensuring the task is done we use the join method.

import time
import multiprocessing

def task():
for i in range(10):
time.sleep(0.3)

def main():
background_task = multiprocessing.Process(target=task)
background_task.start()

... # do the rest stuff that does not depend on the background task

background_task.join() # wait until the background task is done

... # do stuff that depends on the background task

if __name__ == "__main__":
main()

Suppose the main application depends on the parts of the background task. In this case, we need an event-driven design as the join cannot be called multiple times.

import multiprocessing

event = multiprocessing.Event()

def task():
... # synchronous operations

event.set() # notify the main function that the first part of the task is done

... # synchronous operations

event.set() # notify the main function that the second part of the task is also done

... # synchronous operations

def main():
background_task = multiprocessing.Process(target=task)
background_task.start()

... # do the rest stuff that does not depend on the background task

event.wait() # wait until the first part of the background task is done

... # do stuff that depends on the first part of the background task

event.wait() # wait until the second part of the background task is done

... # do stuff that depends on the second part of the background task

background_task.join() # wait until the background task is finally done

... # do stuff that depends on the whole background task

if __name__ == "__main__":
main()

As you already noticed with events we can just provide binary information and those are not effective if the processes are more than two (It will be impossible to know where the event was emitted from). So we use pipes, queues, and manager to provide non-binary information between the processes.



Related Topics



Leave a reply



Submit