How to have a sequential logic with asynchronous?

Asynchronous is often very useful:

  • to avoid freezing the application during the process
  • to be able to cancel a process during its execution

However, the use of asynchronous is more complex and algorithms become more difficult to understand.

Nevertheless, this approach is used more and more.

Only asynchronous mode is supported by Silverlight to call web services!

Imagine the following exercise:

We want to call a service one and only once, put the result in cache and then use the cache.

With a synchronous mode, no problem:

static class Service
{
    private static string _value;
    public static string Value
    {
        get 
        {
            if (_value == null)
                using (ServiceClient service = new ServiceClient())
                {
                    _value = service.GetData(0);
                }
            return _value; 
        }
    }
}

To call it, we can simply write this (in a Console application):

Console.WriteLine(Service.Value);

Ok. Nothing difficult here.

// In this post, I will have to version: one with lambda expressions and one without.

Now, imagine that we want to do the same as previously with asynchronous mode.

To do it, we will use an event based logic:

public static class Service
{
    public static void Init()
    {
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += (sender, e) =>
            {
                if (e.Cancelled)
                    return;
                if (e.Error != null)
                    throw e.Error;
                Value = e.Result;
                if (ValueInitialized != null)
                    ValueInitialized();
            };
            service.GetDataAsync(0);
        }
   
  
    public static string Value { get; private set; } 

   
public static event Action ValueInitialized;
}

public static class Service
{
    public static void Init()
    {
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += service_GetDataCompleted;
            service.GetDataAsync(0);
        }
   

    private static void service_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
    {
        if (e.Cancelled)
            return;
        if (e.Error != null)
            throw e.Error;
        Value = e.Result; 
        if (ValueInitialized != null)
            ValueInitialized();
    }

 

    public static string Value { get; private set; } 
  
    public static event Action ValueInitialized;
}

Now the call is only this:

Service.ValueInitialized += () => Console.WriteLine(Service.Value);
Service.Init();

Service.ValueInitialized += Service_ValueInitialized;
Service.Init();

 

private static void Service_ValueInitialized()
{
    Console.WriteLine(Service.Value);
}

Now, we want to get Service.Value without knowing if it’s already loaded. How to do it?

First, we have to determine if Value is already loaded or not.

To do it, we will add a boolean property IsInitialized:

public static class Service
{
    public static void Init()
   
        if (IsInitialized)
            return;
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += (sender, e) =>
            {
                if (e.Cancelled)
                    return;
                if (e.Error != null)
                    throw e.Error;
                Value = e.Result;

                IsInitialized = true;
                if (ValueInitialized != null)
                    ValueInitialized();
            };
            service.GetDataAsync(0);
        }
   
  
    public static string Value { get; private set; } 

    public static bool IsInitialized { get; private set; } 

   
public static event Action ValueInitialized;
}

public static class Service
{
    public static void Init()
   
        if (IsInitialized)
            return;
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += service_GetDataCompleted;
            service.GetDataAsync(0);
        }
   

    private static void service_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
    {
        if (e.Cancelled)
            return;
        if (e.Error != null)
            throw e.Error;
        Value = e.Result; 

        IsInitialized = true
        if (ValueInitialized != null)
            ValueInitialized();
    }

    public static string Value { get; private set; } 

    public static bool IsInitialized { get; private set; }
  
    public static event Action ValueInitialized;
}

If we look at my initial “specifications”, we want to be sure that the service is called only once. Because of multi-threading, we can call the service more than once.

To fix it, we will call the service in the static constructor:

public static class Service

    static Service()
    {
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += (sender, e) =>
            {
                if (e.Cancelled)
                    return;
                if (e.Error != null)
                    throw e.Error;
                Value = e.Result;

                         IsInitialized = true;
                         if (ValueInitialized != null)
                    ValueInitialized();
            };
            service.GetDataAsync(0);
        }
   
  
    public static string Value { get; private set; } 

    public static bool IsInitialized { get; private set; }


   
public static event Action ValueInitialized;
}

public static class Service
{
    static Service()
    {
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += service_GetDataCompleted;
            service.GetDataAsync(0);
        }
   

    private static void service_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
    {
        if (e.Cancelled)
            return;
        if (e.Error != null)
            throw e.Error;
        Value = e.Result;  
        IsInitialized = true;  
        if (ValueInitialized != null)
            ValueInitialized();
    }

 

    public static string Value { get; private set; } 

    public static bool IsInitialized { get; private set; } 

  
    public static event Action ValueInitialized;
}

To get the Service.Value, we can now use the following code:

Action valueInitialized = () => Console.WriteLine(Service.Value);
if (Service.IsInitialized)
    valueInitialized();
else
    Service.ValueInitialized += valueInitialized;

if (Service.IsInitialized)
    Service_ValueInitialized();
else
    Service.ValueInitialized += Service_ValueInitialized;

private static void Service_ValueInitialized()
{
    Console.WriteLine(Service.Value);
}

However, this isn’t enough. Indeed, because of multi-threading, we can have the following case: the main thread is running. It gets IsInitialized (false) then it’s suspended. Then, the asynchronous thread is running. It sets IsInitialized = true and raises ValueInitialized event. The main thread is running again, wait from ValueInitialized even which was already raised…

01: Action valueInitialized = () => Console.WriteLine(Service.Value);
04: if (Service.IsInitialized)
        valueInitialized();
    else
11:    Service.ValueInitialized += valueInitialized;

02: using (ServiceClient service = new ServiceClient())
    {
        service.GetDataCompleted += (sender, e) =>
        {
05:        if (e.Cancelled)
               return;
06:        if (e.Error != null)
               throw e.Error;
07:        Value = e.Result;

08:                  IsInitialized = true;
09:                  if (ValueInitialized != null)
10:            ValueInitialized();
        };

03:     service.GetDataAsync(0);
    }

So we have to do something for this case:

public static class Service

    static Service()
    {
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += (sender, e) =>
            {
                if (e.Cancelled)
                    return;
                if (e.Error != null)
                    throw e.Error;
                Value = e.Result; 
                lock (LockObject)
                {
                    IsInitialized = true;
                    if (ValueInitialized != null)
                        ValueInitialized();

                }

            };
            service.GetDataAsync(0);
        }
   


   
private static object _lockObject = new object();
    public static object LockObject
    {
        get { return _lockObject; }
    }

              
    public static string Value { get; private set; } 

    public static bool IsInitialized { get; private set; }


   
public static event Action ValueInitialized;
}

public static class Service
{
    static Service()
    {
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += service_GetDataCompleted;
            service.GetDataAsync(0);
        }
   

    private static void service_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
    {
        if (e.Cancelled)
            return;
        if (e.Error != null)
            throw e.Error;
        Value = e.Result;  
        lock (LockObject)
        {
            IsInitialized = true;
            if (ValueInitialized != null)
                ValueInitialized();
        }
   


   
private static object _lockObject = new object();
    public static object LockObject
    {
        get { return _lockObject; }
    }

 
    public static string Value { get; private set; }  

    public static bool IsInitialized { get; private set; }  
   
    public static event Action ValueInitialized;
}

Action valueInitialized = () => Console.WriteLine(Service.Value);
if (Service.IsInitialized)
    valueInitialized ();
lock (Service.LockObject)
{
    if (Service.IsInitialized)
        valueInitialized ();
    else
        Service.ValueInitialized += valueInitialized;
}

if (Service.IsInitialized)
    Service_ValueInitialized();
lock (Service.LockObject)
{
    if (Service.IsInitialized)
        Service_ValueInitialized();
    else
        Service.ValueInitialized += Service_ValueInitialized;
}

private static void Service_ValueInitialized()
{
    Console.WriteLine(Service.Value);
}

// The double test on Service.IsInitialized is useful to avoid useless lock.

However, from an architecture point of view, this code is bad! Indeed, it’s a real issue that classes have to know my Service class code (in order to know the lock logic). Moreover, we can have the same code many times in the solution. So we have to factorize it in Service class.

public static class Service
{
    static Service()
    {
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += (sender, e) =>
            {
                if (e.Cancelled)
                    return;
                if (e.Error != null)
                    throw e.Error;
                Value = e.Result;
                lock (_lockObject)
                {
                    IsInitialized = true;
                    if (ValueInitialized != null)
                        ValueInitialized();
                }
            };
            service.GetDataAsync(0);
        }
   
 

    private static object _lockObject = new object(); 
  
    public static string Value { get; private set; } 
  
    private static bool IsInitialized { get; set; } 
  
    public static void DoWhenInitialized(Action action)
   
        if (action == null)
            return;


        if (IsInitialized)
        {
            action();
            return;
        }

        lock (_lockObject)
        {
            if (IsInitialized)
                action();
            else
                ValueInitialized += action;
        }
   
  
    public static event Action ValueInitialized;
}

public static class Service
{
    static Service()
    {
        using (ServiceClient service = new ServiceClient())
        {
            service.GetDataCompleted += service_GetDataCompleted;
            service.GetDataAsync(0);
        }
   
 
    private static void service_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
    {
        if (e.Cancelled)
            return;
        if (e.Error != null)
            throw e.Error;
        Value = e.Result;
        lock (_lockObject)
        {
            IsInitialized = true;
            if (ValueInitialized != null)
                ValueInitialized();
        }
   

    private static object _lockObject = new object(); 
  
    public static string Value { get; private set; } 
  
    private static bool IsInitialized { get; set; }

    public static void DoWhenInitialized(Action action)
    {
        if (action == null)
            return;
 


        if (IsInitialized)
        {
            action();
            return;
        }

        lock (_lockObject)
        {
            if (IsInitialized)
                action();
            else
                ValueInitialized += action;
        }
   
  

    public static event Action ValueInitialized;
}

The call is now only the following:

Service.DoWhenInitialized(() => Console.WriteLine(Service.Value));

Service.DoWhenInitialized(Service_ValueInitialized);

private static void Service_ValueInitialized()
{
    Console.WriteLine(Service.Value);
}

The last point is very important: don’t forget to remove handler on your events!

So the last version of my code is the following:

public static class Service
{
    static Service()
    {
        using (ServiceClient service = new ServiceClient())
        {
            EventHandler<GetDataCompletedEventArgs> serviceGetDataCompleted = null;
            serviceGetDataCompleted = (sender, e) =>
            {
                service.GetDataCompleted -= serviceGetDataCompleted;
                if (e.Cancelled)
                    return;
                if (e.Error != null)
                    throw e.Error;
                Value = e.Result;
                lock (_lockObject)
                {
                    IsInitialized = true;
                    if (ValueInitialized != null)
                        ValueInitialized();
                }
            };
            service.GetDataCompleted += serviceGetDataCompleted;
            service.GetDataAsync(0);
        }
    }   

    private static object _lockObject = new object(); 
  
    public static string Value { get; private set; } 
  
    private static bool IsInitialized { get; set; } 
  
    public static void DoWhenInitialized(Action action)
    {

        if (action == null)
            return;

        if (IsInitialized)
        {
            action(); 
            return;
        }

        lock (_lockObject)
        {
            if (IsInitialized)
                action();
            else
            {
                Action valueInitialized = null;
                valueInitialized = () =>
                   
{
                        ValueInitialized -= valueInitialized;
                        action(); 
                    };
 
                ValueInitialized += valueInitialized;
            }
        }
    }  
   
    private static event Action ValueInitialized;
}

public static class Service
{
    static Service()
    {
        using (ServiceClient service = new ServiceClient())
        {
            EventHandler<GetDataCompletedEventArgs> serviceGetDataCompleted = null;
            serviceGetDataCompleted = (sender, e) =>
            {
                service.GetDataCompleted -= serviceGetDataCompleted;
                if (e.Cancelled)
                    return;
                if (e.Error != null)
                    throw e.Error;
                Value = e.Result;
                lock (_lockObject)
                {
                    IsInitialized = true;
                    if (ValueInitialized != null)
                        ValueInitialized();
                }
            };
            service.GetDataCompleted += serviceGetDataCompleted;
            service.GetDataAsync(0);
        }
   
 
    private static object _lockObject = new object(); 
 
    public static string Value { get; private set; } 
 
    private static bool IsInitialized { get; set; } 
 
    public static void DoWhenInitialized(Action action)
    {
        if (action == null)
            return;

        if (IsInitialized)
        {
            action(); 
            return;
        }

        lock (_lockObject)
        {
            if (IsInitialized)
                action();
            else 
                ValueInitialized += new ActionClass { Action = action }.Service_ValueInitialized;  
        }
   
 
 
    private static event Action ValueInitialized; 
 
    private class ActionClass
    {
        public Action Action { get; set; } 
 
        public void Service_ValueInitialized()
        {
            ValueInitialized -= Service_ValueInitialized;
            if (Action != null)
                Action();
        }
    }

}

//It’s so painful without lambda…

Enjoy [:)]

This entry was posted in 10500, 7671, 7672. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>