Async question

This forum is meant for questions and discussions about the X# language and tools
HansjoergP
Posts: 142
Joined: Mon Jul 25, 2016 3:58 pm
Location: Italy

Async question

Post by HansjoergP »

If it is not a GUI thread. Then the .Result call should work. The Result call blocks the execution until the result is available. You have to be carefull, because depending on your thread it can result to an deadlock.
Another possibility would be "Task.Run( () => asyncMethod()).Wait();"

async/await is a great thing, but everything with parallel execution requires a lot of attention and sometimes it is really hard to understand

https://learn.microsoft.com/en-us/dotne ... rom-a-task
https://stackoverflow.com/questions/172 ... -deadlocks
User avatar
robert
Posts: 4520
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

Async question

Post by robert »

Wolfgang,
If you want the main thread to wait until the asynchronous code finishes, then you are making things sychronous and your app will "halt" while the background code is running.
The whole idea about asynchronous is that the main thread will NOT wait and will be free for the user.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Async question

Post by wriedmann »

Hi Robert,
my main problem is that in the async version of the class all calls are async, even the one I don't like to have async, like connect, change directory, directory listing, current directory and so forth. I would like to have async only the transfer methods (upload and download).
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
HansjoergP
Posts: 142
Joined: Mon Jul 25, 2016 3:58 pm
Location: Italy

Async question

Post by HansjoergP »

User avatar
robert
Posts: 4520
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

Async question

Post by robert »

Wolfgang,
Inside Visual Studio we sometimes have the same problem.
In those cases we use a helper method from the ThreadHelpers JoinableTaskFactory.
That looks like this:

Code: Select all

            ThreadHelper.JoinableTaskFactory.Run(async delegate
                {
                    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                    using (new AutomationScope(this.project.Site))
                    {
                        project.SetEditLabel(value);
                    }
                });
I think you can do something similar with the Task.Run method.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Async question

Post by wriedmann »

Hi Hansjörg,

thank you very much, this lik is also helpful to understand the concept better:
https://blog.stephencleary.com/2012/02/ ... await.html
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Async question

Post by wriedmann »

Hi Robert,
I will look into it and then post the code here (if I am able to make it work).
Thank you very much!
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
User avatar
SHirsch
Posts: 286
Joined: Tue Jan 30, 2018 8:23 am
Location: Germany

Async question

Post by SHirsch »

Hi all,

here is a good description of asynchronos programming:
https://learn.microsoft.com/en-us/dotne ... ogramming/

Stefan
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Async question

Post by wriedmann »

Hi,
I have something very strange. Please see this code:

Code: Select all

oStopwatch := System.Diagnostics.Stopwatch{}
oStopwatch:Start()
Task.Run( { => self:_ConnectA( cHostName, cUserName, cPassword, nPort, oConfig ) } )
while _lWorking
	System.Threading.Thread.Sleep( 10 )
	if oStopwatch:Elapsed:Seconds > _nTimeout
		_cErrorMessage			:= "Timeout " + _nTimeout:ToString()
		exit
	endif
end
_DebOut( "Waited " + oStopwatch:Elapsed:ToString() )
and the called code is

Code: Select all

_DebOut( "Creating AsyncFtpClient" )
_oClient			:= AsyncFtpClient{ cHostName, cUserName, cPassword, nPort, oConfig }
_DebOut( "Creating Task Connect()" )
oTask				:= _oClient:Connect()
_DebOut( "Awaiting Task Connect()" )
await oTask
_DebOut( "Task Connect() finished" )
The really strange thing is the sequence:

Code: Select all

Waited 00:00:00.0029780
Client is null
Creating AsyncFtpClient
Creating Task Connect()
Awaiting Task Connect()
Task Connect() finished
This means that first the rest of the method is executed before the task is started!
I'm now searching a possibility to make my method wait and let the task run in the background.
Wolfgang
P.S. even this call does not resolves:

Code: Select all

Task.WhenAll( self:_ConnectA( cHostName, cUserName, cPassword, nPort, oConfig ) )
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

Async question

Post by NickFriend »

Hi Wolfgang,

What Robert said - you need to un-async your async ;-)

The following class will do what you want.

Code: Select all

    public static class AsyncHelpers
    {
        /// <summary>
        /// Executes an async Task<T> method which has a void return value synchronously
        /// </summary>
        /// <param name="task">Task<T> method to execute</param>
        public static void RunSync(Func<Task> task)
        {
            var oldContext = SynchronizationContext.Current;
            var synch = new ExclusiveSynchronizationContext();
            SynchronizationContext.SetSynchronizationContext(synch);
            synch.Post(async _ =>
            {
                try
                {
                    await task();
                }
                catch (Exception e)
                {
                    synch.InnerException = e;
                    throw;
                }
                finally
                {
                    synch.EndMessageLoop();
                }
            }, null);
            synch.BeginMessageLoop();

            SynchronizationContext.SetSynchronizationContext(oldContext);
        }

        /// <summary>
        /// Executes an async Task<T> method which has a T return type synchronously
        /// </summary>
        /// <typeparam name="T">Return Type</typeparam>
        /// <param name="task">Task<T> method to execute</param>
        /// <returns></returns>
        public static T RunSync<T>(Func<Task<T>> task)
        {
            var oldContext = SynchronizationContext.Current;
            var synch = new ExclusiveSynchronizationContext();
            SynchronizationContext.SetSynchronizationContext(synch);
            T ret = default(T);
            synch.Post(async _ =>
            {
                try
                {
                    ret = await task();
                }
                catch (Exception e)
                {
                    synch.InnerException = e;
                    throw;
                }
                finally
                {
                    synch.EndMessageLoop();
                }
            }, null);
            synch.BeginMessageLoop();
            SynchronizationContext.SetSynchronizationContext(oldContext);
            return ret;
        }

        private class ExclusiveSynchronizationContext : SynchronizationContext
        {
            private bool done;
            public Exception InnerException { get; set; }
            readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
            readonly Queue<Tuple<SendOrPostCallback, object>> items =
                new Queue<Tuple<SendOrPostCallback, object>>();

            public override void Send(SendOrPostCallback d, object state)
            {
                throw new NotSupportedException("We cannot send to our same thread");
            }

            public override void Post(SendOrPostCallback d, object state)
            {
                lock (items)
                {
                    items.Enqueue(Tuple.Create(d, state));
                }
                workItemsWaiting.Set();
            }

            public void EndMessageLoop()
            {
                Post(_ => done = true, null);
            }

            public void BeginMessageLoop()
            {
                while (!done)
                {
                    Tuple<SendOrPostCallback, object> task = null;
                    lock (items)
                    {
                        if (items.Count > 0)
                        {
                            task = items.Dequeue();
                        }
                    }
                    if (task != null)
                    {
                        task.Item1(task.Item2);
                        if (InnerException != null) // the method threw an exeption
                        {
                            throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
                        }
                    }
                    else
                    {
                        workItemsWaiting.WaitOne();
                    }
                }
            }

            public override SynchronizationContext CreateCopy()
            {
                return this;
            }
        }
    }
Then call it like this...

AsyncHelpers.RunSync(() => this.MyMethodAsync());

Apologies for the C# and the semi-colons! This is not my code originally (StackOverFlow I think), but it works perfectly.

Nick
Post Reply