0

I am using this library as wrapper for Mailchimp API v3.0. This library has all methods as async.

But I have to call that methods from my synchronous methods and to read result.

The first I have to say that I CANNOT make my methods as async. So my methods must stay synchronous and they will call async methods from this library.

So I read a lot of documentation about deadlock and avoiding that.

So here is one example of the method from the library:

internal class MemberLogic : BaseLogic, IMemberLogic
{
        public async Task<Member> AddOrUpdateAsync(string listId, Member member)
        {
            using (var client = this.CreateMailClient($"{BaseUrl}/"))
            {
                var response =
                await
                client.PutAsJsonAsync($"{listId}/members/{this.Hash(member.EmailAddress.ToLower())}", member, null).ConfigureAwait(false);

                await response.EnsureSuccessMailChimpAsync().ConfigureAwait(false);

                return await response.Content.ReadAsAsync<Member>().ConfigureAwait(false);
            }
        }
}

Not I am calling this method from my method like:

public bool ListSubscribe(string email, string listId, string apiKey)
{
    try
    {
       var api = new MailChimpManager(apiKey);
       var profile = new Member();
       profile.EmailAddress = emailAddress;
       Member member = api.Members.AddOrUpdateAsync(listId, profile).Result;
    }
    catch(Exception ex)
    {
         throw;
    }

     return member != null;
}

The code deadlocks at line which calls async method:

Member member = api.Members.AddOrUpdateAsync(listId, profile).Result;

So my question is there any I am doing wrong in calling async method from sync?? Because I just cannot get this working.

UPDATE:

Regarding to these articles:

http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

https://blog.ciber.no/2014/05/19/using-task-configureawaitfalse-to-prevent-deadlocks-in-async-code/

I should be able to use it like in my example above with just setting .ConfigureAwait(false);

What is not mentioned in the question which is marked as duplicated.

Also this my code is working with a few other async methods fine. It just doesn't work with this just one method and it deadlocks in case Exception is occurred in async method.

carpics
  • 2,272
  • 4
  • 28
  • 56
  • 1
    *"I CANNOT make my methods as async"* Why not? Is it you just don't want to do the work of changing it or is there some other reason? – Scott Chamberlain Aug 16 '16 at 14:29
  • 3
    You are mixing async and sync calls. It's either you go async all the way or sync all the way. once you mix them you will get deadlocks. – Nkosi Aug 16 '16 at 14:34
  • @ScottChamberlain Yes it's just that I don't want, because that would be really painful to do at this point. – carpics Aug 16 '16 at 14:38
  • @Nkosi There must be some way to call async method from synced and to get result. I am new with this, but sorry I just cannot believe that there is no way to do that. – carpics Aug 16 '16 at 14:40
  • @carpics read this article https://msdn.microsoft.com/en-us/magazine/jj991977.aspx – Nkosi Aug 16 '16 at 14:42
  • 3
    Besides all the said above, you can try to call the `AddOrUpdateAsync` method inside another task using `Task.Factory.StartNew` method with a non-default scheduler and then `Unwrap` the task in order to get result. – galenus Aug 16 '16 at 14:43
  • 2
    If you can't believe it, you're just admitting that you know very little of what goes on behind the hood - you might want to read up on how async works, and how it deals with synchronization contexts, and how synchronization contexts work in an ASP.NET MVC application. You're trying to have two separate asynchronous workflows access the same single-user object at the same time - *of course* it's going to deadlock. – Luaan Aug 16 '16 at 14:45
  • @galenus `Task.Factory.StartNew` will *ignore* the result of the asynchronous computation and will not wait for it to complete. You can replicate the same thing quite easily yourself - just remove the `.Result` and you get the exact same thing. `Task.Run` will work a bit better, but it's still a very bad idea, and you need to understand the consequences which carpics clearly doesn't at this point. – Luaan Aug 16 '16 at 14:45
  • `Member member = new TaskFactory(CancellationToken.None,TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default).StartNew(() => api.Members.AddOrUpdateAsync(listId, profile)).Unwrap().GetAwaiter().GetResult();` – Igor Aug 16 '16 at 14:46
  • @Luaan I get what you say, and you are right. Task>.Factory.StartNew will return a task of task which can be unwrapped, but unwrapping will result in the same problem... Task.Run does not provide the scheduler customization, however. What can be done is using Task.Factory in conjunction with TaskCompletionSource, don't you think. Because sometimes there is no way to use async everywhere until you rewrite the whole codebase. – galenus Aug 16 '16 at 14:54
  • @carpics look at [this gist](https://gist.github.com/galenus/85ebefafa86bb540bb2fd917456961ee), I believe it will solve your problem – galenus Aug 16 '16 at 15:53

0 Answers0