Click here to Skip to main content
15,888,177 members
Please Sign up or sign in to vote.
2.00/5 (1 vote)
Javascript is interpreted language and can do asynchronous non-blocking callbacks like:

JavaScript
/* --- in a script tag --- */
function hello9000(msg, callback)
{
        console.log("hello9000 started");
        setTimeout(function()
        {
                callback(msg);
        }, 9000);
        console.log("hello9000 loading");
}

function hello6000(msg, callback)
{
        console.log("hello6000 started");
        setTimeout(function()
        {
                callback(msg);
        }, 6000);
        console.log("hello6000 loading");
}

function hello3000(msg, callback)
{
        console.log("hello3000 started");
        setTimeout(function()
        {
                callback(msg);
        }, 3000);
        console.log("hello3000 loading");
}

hello9000("hello9000 ended", function(msg)
{
        console.log(msg);
});

hello6000("hello6000 ended", function(msg)
{
        console.log(msg);
});

hello3000("hello3000 ended", function(msg)
{
        console.log(msg);
});


C# is compiled just in time language and you can do asynchronous non-blocking callbacks too. But there is one problem - When I do not use threads it's blocking the whole code. I can do it only with manually making new threads for each function containing callback.

My code "equivalent" (although not really equivalent) for the above is:

C#
/* --- in the class --- */
public static void hello9000(string message)
{
        Console.WriteLine("hello9000 loading");
        //this throttling isn't correct
        Stopwatch timer = new Stopwatch();
        timer.Start();
        while (timer.ElapsedMilliseconds < 9000){}
        timer.Stop();
        Console.WriteLine(message);
}
        
public static void hello6000(string message)
{
        Console.WriteLine("hello6000 loading");
        //this throttling isn't correct
        Stopwatch timer = new Stopwatch();
        timer.Start();
        while (timer.ElapsedMilliseconds < 6000){}
        timer.Stop();
        Console.WriteLine(message);
}
        
public static void hello3000(string message)
{
        Console.WriteLine("hello3000 loading");
        //this throttling isn't correct
        Stopwatch timer = new Stopwatch();
        timer.Start();
        while (timer.ElapsedMilliseconds < 3000){}
        timer.Stop();
        Console.WriteLine(message);
}
        
public static void printName(string message, Action<string> callback)
{
        callback(message);
}

/* --- in the main function --- */
/* 
this for instance is blocking
printName("hello9000 ended", hello9000);
printName("hello6000 ended", hello6000);
printName("hello3000 ended", hello3000);
*/
Thread thread9000 = new Thread(new ThreadStart(delegate()
{
        printName("hello9000 ended", hello9000);
}));
Thread thread6000 = new Thread(new ThreadStart(delegate()
{
        printName("hello6000 ended", hello6000);
}));
Thread thread3000 = new Thread(new ThreadStart(delegate()
{
        printName("hello3000 ended", hello3000);
}));

thread9000.Start();
thread6000.Start();
thread3000.Start();


Is there a way of doing this type callback calling non-blocking without the need of making threads manually? Does that mean that Javascript is making threads automatically when detect such pattern? What about Java - it's acting like C# (or C# acts like Java):

Java
//only the threads
Thread hello9000 = new Thread(new Runnable()
{
        public void run()
        {
                System.out.println("hello9000 started");
                try
                {
                        Thread.sleep(9000);
                }
                catch(InterruptedException exc){
                        System.out.println(exc);
                }
                System.out.println("hello9000 ended");
                return;
        }
});
Thread hello6000 = new Thread(new Runnable()
{
        public void run()
        {
                System.out.println("hello6000 started");
                try
                {
                    Thread.sleep(6000);
                }
                catch(InterruptedException exc)
                {
                    System.out.println(exc);
                }
                System.out.println("hello6000 ended");
                return;
        }
});
Thread hello3000 = new Thread(new Runnable()
{
        public void run()
        {
                System.out.println("hello3000 started");
                try
                {
                    Thread.sleep(3000);
                }
                catch(InterruptedException exc)
                {
                    System.out.println(exc);
                }
                System.out.println("hello3000 ended");
                return;
        }
});

hello9000.start();
hello6000.start();
hello3000.start();
Posted
Updated 11-Jan-16 5:40am
v7
Comments
F-ES Sitecore 7-Jan-16 11:08am    
Google "asynchronous code c#" and you'll find examples of using the "await" keyword etc to do asynchronous code without manual thread management.
Member 0123456789 7-Jan-16 12:03pm    
Doesn't seem to find the correct information about using callbacks with async/await (the delegation doesn't work);
John C Rayan 7-Jan-16 11:14am    
Hi
it depends what version of C# you are using. As F-ES Sitecore has pointed out the async code is made lot easier in latest versions of C#.
Member 0123456789 7-Jan-16 12:07pm    
.NET Framework 4 (lambdas are harder than delegations - but will start using them if it's possible to call function containing callback non-blocking without threads).

You mean something like this? What's better about this method is you get to handle exceptions as well as timeouts quite cleanly.
C#
private async void StartButton_Click(object sender, EventArgs e)
{
    try
    {
        string message;
        var cancelToken = new CancellationTokenSource();

        cancelToken.CancelAfter(10000);
        message = await WorkerMethod(cancelToken.Token);
        PostMessage(message);

        cancelToken.CancelAfter(2000);
        message = await WorkerMethod(cancelToken.Token);
        PostMessage(message);
    }
    catch (OperationCanceledException)
    {
        PostMessage("Worker was canceled due to timeout.");
    }
    catch (Exception ex)
    {
        PostMessage(string.Format("Worker failed with exception '{0}'.", ex.GetType().ToString()));
    }
}

static async Task<string> WorkerMethod(CancellationToken cancelToken)
{
    // Simulated work... Delays for 5 seconds and returns a message.
    // Of course, if the cancellation token expires first, this task
    // will be canceled before it completes.
    await Task.Delay(5000, cancelToken);

    return "Task completed...";
}

private void PostMessage(string message)
{
    textBox1.Text += message + Environment.NewLine;
}</string>
 
Share this answer
 
v2
The answer, thanks to F-ES Sitecore, John C Rayan, Richard Deeming and Google (still overkill, but better than nothing):

C#
/* --- in the class --- */
public static async Task hello9000(string message)
{
        await Task.Run(delegate()
        {
                Console.WriteLine("hello9000 loading");
                //this throttling isn't correct
                Stopwatch timer = new Stopwatch();
                timer.Start();
                while (timer.ElapsedMilliseconds < 9000){}
                timer.Stop();
                Console.WriteLine(message);
        });
}
        
public static async Task hello6000(string message)
{
        await Task.Run(delegate()
        {
                Console.WriteLine("hello6000 loading");
                //this throttling isn't correct
                 Stopwatch timer = new Stopwatch();
                timer.Start();
                while (timer.ElapsedMilliseconds < 6000){}
                timer.Stop();
                Console.WriteLine(message);
        });
}
        
public static async Task hello3000(string message)
{
        await Task.Run(delegate()
        {
                Console.WriteLine("hello3000 loading");
                //this throttling isn't correct
                Stopwatch timer = new Stopwatch();
                timer.Start();
                while (timer.ElapsedMilliseconds < 3000){}
                timer.Stop();
                Console.WriteLine(message);
        });
}
        
public static async void printName(string message, Func<string, Task> callback)
{
        await callback(message);
}

/* --- in the main function --- */
Task task9000 = printName("hello9000 ended", hello9000);
Task task6000 = printName("hello6000 ended", hello6000);
Task task3000 = printName("hello3000 ended", hello3000);

/* --- the main function doesn't wait the async functions, so additionally you have wait with something like this --- */
Task.WaitAll(task9000, task6000, task3000);
/* --- or separately ---
task9000.Wait();
task6000.Wait();
task3000.Wait();
*/
 
Share this answer
 
v6
It's actually a lot simpler than Solution #1:
C#
public static async Task HelloDelay(string message, Action<string> callback, int millisecondsDelay)
{
    Console.WriteLine("HelloDelay({0}) loading", millisecondsDelay);
    await Task.Delay(millisecondsDelay);
    callback(message);
}

public static async Task Run()
{
    var task9000 = HelloDelay("HelloDelay(9000) ended", Console.WriteLine, 9000);
    var task6000 = HelloDelay("HelloDelay(6000) ended", Console.WriteLine, 6000);
    var task3000 = HelloDelay("HelloDelay(3000) ended", Console.WriteLine, 3000);
    await Task.WhenAll(task9000, task6000, task3000);
}

Use the Task.Delay method[^] to pause the task, rather than sitting in a tight loop checking the elapsed time.

That pushes the execution onto a continuation, so you no longer need the Task.Run call.

Avoid "async void" wherever possible: Avoid async void methods - Phil Haack[^]

I've merged the three "hello" methods into a single method, since they only vary by the delay time, which can be passed as a parameter. If necessary, you could create three copies of the method without the millisecondsDelay parameter, and hard-code the delay within the methods.
 
Share this answer
 
Comments
Member 0123456789 8-Jan-16 15:53pm    
The function HelloDelay is the closing function for the callback. The function Run is container of Tasks for the callbacks, which are Console.WriteLine. This will only output the messages asynchronously, while I intended a custom callback functions (declared or anonymous), which I can pass separately on a model of callback containing function, so I can call the last asynchronously.

function container(string message, callback)
{
/* do something */
callback(message);//when the above finish call me through function pointer
}
function callback(string message)
{
/* do something */
}

/* do something */
container("hello", callback);//start this without blocking the code
/* do something */

/* ...somewhere in the time and space container function finish... */

Simple as it is, not yet simple for most languages, not build this way.
Richard Deeming 8-Jan-16 16:00pm    
So something like:
async Task Container(string message, Func<string, Task> callback)
{
await Task.Yield(); // Don't block the caller
/* Do something */
await callback(message);
}

async Task Callback(string message)
{
/* Do something */
}

async Task Run()
{
/* Do something */
var theTask = Container("hello", Callback);
/* Do something else */
await theTask;
}


Task.Yield method[^]
Member 0123456789 8-Jan-16 16:07pm    
This works perfectly - although probably is really wrong architecture for C#:

public static async Task hello9000(string name, string message)
{
await Task.Run(delegate()
{
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.ElapsedMilliseconds < 9000)
{
//wait, wait and wait...
}
timer.Stop();
//Console.WriteLine(name);
Console.WriteLine(message);
});
}

public static async Task hello6000(string name, string message)
{
await Task.Run(delegate()
{
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.ElapsedMilliseconds < 6000)
{
//wait, wait and wait...
}
timer.Stop();
//Console.WriteLine(name);
Console.WriteLine(message);
});
}

public static async Task hello3000(string name, string message)
{
await Task.Run(delegate()
{
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.ElapsedMilliseconds < 3000)
{
//wait, wait and wait...
}
timer.Stop();
//Console.WriteLine(name);
Console.WriteLine(message);
});
}

public static async Task printName(string name, string message, Func<string, string, Task> callback)
{
Console.WriteLine(name + " started on thread " + System.Threading.Thread.CurrentThread.ManagedThreadId);
await callback(name, message);
}


printName("hello9000", "hello9000 ended on thread " + System.Threading.Thread.CurrentThread.ManagedThreadId, hello9000);
printName("hello6000", "hello6000 ended on thread " + System.Threading.Thread.CurrentThread.ManagedThreadId, hello6000);
printName("hello3000", "hello3000 ended on thread " + System.Threading.Thread.CurrentThread.ManagedThreadId, hello3000);
Richard Deeming 8-Jan-16 16:11pm    
It might work, but watch the CPU usage - you're sitting in a tight loop waiting for 9 seconds, like a kid on a car trip shouting "Are we nearly there yet?" over and over again! :)

Much better to let the CPU get on with doing other things while you're waiting:

public static async Task hello9000(string name, string message)
{
await Task.Delay(9000);
Console.WriteLine(message);
}
Member 0123456789 8-Jan-16 16:15pm    
You're completely right - there will be something else - like piping streams or some kind of manipulating data. With the above code I get error while compiling the code in the console - "Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.", while with the code in Solution 1 the compiler doesn't catch it. Both works.
C#
/* asynchronous callback*/
public static async Task AsyncCallback<P>(P parameter, Action<P> action)
{
    await Task.Run(delegate()
    {
        action(parameter);
    });
}
 
/* to be used in async function as */
await AsyncCallback<string[]>(new string[]{"Hello,", "World!"}, delegate(string[] p)
{
    Console.WriteLine(string.Concat(p[0], ' ', p[1]));
});
 
/* to be used in sync function as */
Task task = AsyncCallback<string[]>(new string[]{"Hello,", "World!"}, delegate(string[] p)
{
    Console.WriteLine(string.Concat(p[0], ' ', p[1]));
});
task.Wait();
 
/* bonus - return asynchronous callback */
public static async Task<R> AsyncReturnCallback<P, R>(P parameter, Func<P, R> action)
{
    R r = default(R);
    await Task.Run(delegate()
    {
        r = action(parameter);
    });
    return r;
}
 
/* to be used in async function as */
string result = await AsyncReturnCallback<string[], string>(new string[]{"Hello,", "World!"}, delegate(string[] p)
{
    return string.Concat(p[0], ' ', p[1]);
});
 
/* to be used in sync function as */
Task<string> task = AsyncReturnCallback<string[], string>(new string[]{"Hello,", "World!"}, delegate(string[] p)
{
    return string.Concat(p[0], ' ', p[1]);
});
task.Wait();
string result = task.Result;
 
/* both can be used with List<task> and/or Task.WaitAll()/Task.WaitAny() if you have to spawn and/or wait many/any */
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900