I'm trying to get the result of this queries to redis (using stackexchange C# client) in parallel but somehow I'm running in deadlock and not sure where
The method for retrieving the data:
public LiveData Get(string sessionId)
{
return GetAsync(sessionId).Result;
}
private async Task<LiveData> GetAsync(string sessionId)
{
var basketTask = GetBasketAsync(sessionId);
var historyTask = GetHistoryAsync(sessionId);
var capturedDataTask = GetCapturedDataAsync(sessionId);
var basket = await basketTask;
var history = await historyTask;
var capturedData = await capturedDataTask;
return new LiveData
{
Basket = basket.IsNullOrEmpty
? new List<Product>()
: JsonConvert.DeserializeObject<List<Product>>(basket),
History = history.Select(cachedProduct
=> JsonConvert.DeserializeObject<Product>(cachedProduct.Value.ToString())).ToList(),
CapturedData = capturedData.ToDictionary<HashEntry, string, object>(
hash => hash.Name, hash => JsonConvert.DeserializeObject(hash.Value))
};
}
And the methods for fetching the cached data from redis are:
private async Task<RedisValue> GetBasketAsync(string key)
{
key = $"{key}{BasketSuffix}";
var redisDb = RedisConnection.Connection.GetDatabase();
redisDb.KeyExpireAsync(key, _expire);
return await redisDb.StringGetAsync(key);
}
private async Task<HashEntry[]> GetHistoryAsync(string key)
{
key = $"{key}{HistorySuffix}";
var redisDb = RedisConnection.Connection.GetDatabase();
redisDb.KeyExpireAsync(key, _expire);
return await redisDb.HashGetAllAsync(key);
}
private async Task<HashEntry[]> GetCapturedDataAsync(string key)
{
key = $"{key}{CapturedDataSuffix}";
var redisDb = RedisConnection.Connection.GetDatabase();
redisDb.KeyExpireAsync(key, _expire);
return await redisDb.HashGetAllAsync(key);
}
I think it's fine calling the KeyExpireAsync like this, just because it's fine to trigger and forget but not sure if that could be related (I tried even removing it and it's still blocked)
The source of the deadlock is this snippet:
public LiveData Get(string sessionId)
{
return GetAsync(sessionId).Result;
}
Instead, invoke it the proper way "async all the way":
public async Task<LiveData> Get(string sessionId)
{
return await GetAsync(sessionId);
}
Invoking .Result can lead to deadlocking, as can using the .Wait() API. Also, from the looks of it -- the .KeyExpireAsync needs to be awaited.
async Task<RedisValue> GetBasketAsync(string key)
{
key = $"{key}{BasketSuffix}";
var redisDb = RedisConnection.Connection.GetDatabase();
await redisDb.KeyExpireAsync(key, _expire);
return await redisDb.StringGetAsync(key);
}
I understand your thought process on not using the await keyword on the .KeyExpireAsync call but if I were writing this code I would most certainly want to await it like I have demonstrated. It is a code smell to have a fire-and-forget, and can be easily avoided.
Related
I have problem with calling API in my Discord Bot, I am trying to learn async, await but it seems as I am stuck.
This is my Call API class where I am calling API.
public async Task<Root> GetInfoAsync()
{
string path = "https://onemocneni-aktualne.mzcr.cz/api/v2/covid-19/zakladni-prehled.json";
Root data = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<Root>(json);
}
return data;
}
public async Task<string> VypisAsync()
{
Root data = await this.GetInfoAsync();
int deaths = data.data[0].umrti,
recovered = data.data[0].vyleceni,
positive = data.data[0].aktivni_pripady,
hospitalized = data.data[0].aktualne_hospitalizovani;
return $"Aktualni situace v ČR:\n" +
$"vyleceni: {recovered}\n" +
$"aktualne nemocni: {positive}\n" +
$"hospitalizovani: {hospitalized}\n" +
$"zemreli: {deaths}";
}
And here is my covid command
public class CovidModule : ModuleBase<SocketCommandContext>
{
// ~void -> current covid situation in CZ
[Command("covid")]
[Summary("shows current covid situation in CZ")]
public async Task<Task> CovidAsync()
{
CovidApi api = new CovidApi();
return ReplyAsync(await api.VypisAsync());
}
}
I know that all others commands return Task, but I don't know how to make it like that.
ReplyAsync() is an async method, so you need to await it.
Your CovidAsync() method isn't returning an actual value, so in the synchronous world its return value would be void. Since it's an async method you return Task instead.
public async Task CovidAsync()
{
CovidApi api = new CovidApi();
await ReplyAsync(await api.VypisAsync());
}
As an aside, it would be better to have CovidApi as a member of your CovidModule. That way you don't need to keep newing it up in each method.
I have a Task which starts a win process, which generates file if its not created yet and returns it. The problem is that the action is called more than once. To be more precisely its src attribute of a <track> element.
I have ConcurrentDictionary<Guid, Task<string>>
which keeps track of for which Id a process is currently running
public async Task<string> GenerateVTTFile(Guid Id)
{
if (_currentGenerators.TryGetValue(id, out Task<string> task))
{
return await task; // problem is here?
}
var t = Run(); // Task
_currentGenerators.TryAdd(id, t);
return await t;
}
In the action method of the controller
var path = await _svc.GetSomePath();
if (string.IsNullOrEmpty(path))
{
var path = await svc.GenerateVTTFile(id);
return PhysicalFile(path, "text/vtt");
}
return PhysicalFile(path, "text/vtt");
Run() method is just starting Process and waits it.
process.WaitForExit();
What I want to achieve is to return the result of the same task for the same Id. It seems that if the Id already exists in the dictionary and I await it starts another process (calls Run method again).
Is there a way to achieve that?
You can make the method atomic to protect the "dangerzone":
private SemaphoreSlim _sem = new SemaphoreSlim(1);
public Task<string> GenerateVTTFile(Guid Id)
{
_sem.Wait();
try
{
if (_currentGenerators.TryGetValue(Id, out Task<string> task))
{
return task;
}
var t = Run(); // Task
_currentGenerators.TryAdd(Id, t); // While Thread 1 is here,
// Thread 2 can already be past the check above ...
// unless we make the method atomic like here.
return t;
}
finally
{
_sem.Release();
}
}
Drawback here is, that also calls with different ids have to wait. So that makes for a bottleneck. Of course, you could make an effort but hey: the dotnet guys did it for you:
Preferably, you can use GetOrAdd to do the same with only ConcurrentDictionary's methods:
public Task<string> GenerateVTTFile(Guid Id)
{
// EDIT: This overload vv is actually NOT atomic!
// DO NOT USE:
//return _currentGenerators.GetOrAdd(Id, () => Run());
// Instead:
return _currentGenerators.GetOrAdd(Id,
_ => new Lazy<Task<string>>(() => Run(id))).Value;
// Fix "stolen" from Theodore Zoulias' Answer. Link to his answer below.
// If you find this helped you, please upvote HIS answer.
}
Yes, it's really a "one-liner".
Please see this answer: https://stackoverflow.com/a/61372518/982149 from which I took the fix for my flawed answer.
As pointed out already by João Reis, using simply the GetOrAdd method is not enough to ensure that a Task will be created only once per key. From the documentation:
If you call GetOrAdd simultaneously on different threads, valueFactory may be called multiple times, but only one key/value pair will be added to the dictionary.
The quick and lazy way to deal with this problem is to use the Lazy class. Instead of storing Task objects in the dictionary, you could store Lazy<Task> wrappers. This way even if a wrapper is created multiple times per key, all extraneous wrappers will be discarded without their Value property requested, and so without duplicate tasks created.
private ConcurrentDictionary<Guid, <Lazy<Task<string>>> _currentGenerators;
public Task<string> GenerateVTTFileAsync(Guid id)
{
return _currentGenerators.GetOrAdd(id,
_ => new Lazy<Task<string>>(() => Run(id))).Value;
}
In order to have multiple concurrent calls of that method but only one for each id, you need to use ConcurrentDictionary.GetOrAdd with SemaphoreSlim.
GetOrAdd is not enough because the factory parameter might be executed more than once, see "Remarks" here https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.getoradd?view=netframework-4.8
Here is an example:
private ConcurrentDictionary<Guid, Generator> _currentGenerators =
new ConcurrentDictionary<Guid, Generator>();
public async Task<string> GenerateVTTFile(Guid id)
{
var generator = _currentGenerators.GetOrAdd(id, _ => new Generator());
return await generator.RunGenerator().ConfigureAwait(false);
}
public class Generator
{
private int _started = 0;
private Task<string> _task;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
public async Task<string> RunGenerator()
{
if (!IsInitialized())
{
await Initialize().ConfigureAwait(false);
}
return await Interlocked.CompareExchange(ref _task, null, null).ConfigureAwait(false);
}
private async Task Initialize()
{
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
// check again after acquiring the lock
if (IsInitialized())
{
return;
}
var task = Run();
_ = Interlocked.Exchange(ref _task, task);
Interlocked.Exchange(ref _started, 1);
}
finally
{
_semaphore.Release();
}
}
private bool IsInitialized()
{
return Interlocked.CompareExchange(ref _started, 0, 0) == 1;
}
private async Task<string> Run()
{
// your implementation here
}
}
I am trying to write a function which uses Task and TaskCompletion.
My problem is that after login, the result is not returned. I used similar code before and it was working. I do not know what causes for this situation.
public async Task<byte[]> Sign(byte[] documentContent)
{
var service = new SignServiceWrapper("https://example.com?wsdl");
var loginResult = await Task.Run(() => service.Login(loginRequest));
//....
}
and my SignServiceWrapper class
public class SignServiceWrapper
{
private static string _webServiceUrl;
private BrokerClientClient client;
public SignServiceWrapper(string webServiceUrl)
{
_webServiceUrl = webServiceUrl;
}
public Task<loginResponse> Login(loginRequest request)
{
var tcs = new TaskCompletionSource<loginResponse>();
ClientGenerator.WebServiceUrl = _webServiceUrl;
ClientGenerator.InitializeService();
client = ClientGenerator.ServiceClient;
client.loginCompleted += (sender, loginResult) =>
{
if (loginResult.Error != null)
tcs.SetException(loginResult.Error);
else
tcs.TrySetResult(loginResult.Result);
};
client.loginAsync(request);
return tcs.Task;
}
// ...
}
If I call my login function like that it works
var loginResult = Task.Run(() => service.Login(loginRequest));
loginResult.Wait();
I know that there is kind of a deadlock but I don't know how to solve this here and which object.
Here is a working .NET Fiddle.
I think your .Login method is trying to do too much. The first thing that I noticed (and can only imagine how it's implemented) is the static ClientGenerator, that has static mutable state. This which is alarming and a very specific code smell. I would love to see what the client itself looks like and how that is implemented as that would certainly help to better answer this question.
Based on what you shared thus far (and assuming that the client.loginAsync returns a Task<loginResponse>), I would say that you could do the following:
public class SignServiceWrapper
{
private static string _webServiceUrl;
private BrokerClientClient client;
public SignServiceWrapper(string webServiceUrl)
{
_webServiceUrl = webServiceUrl;
}
public Task<loginResponse> LoginAsync(loginRequest request)
{
ClientGenerator.WebServiceUrl = _webServiceUrl;
ClientGenerator.InitializeService();
client = ClientGenerator.ServiceClient;
return client.loginAsync(request);
}
// ...
}
You could then consume this as such:
public async Task<byte[]> Sign(byte[] documentContent)
{
var service = new SignServiceWrapper("https://example.com?wsdl");
var loginResult = await service.LoginAsync(loginRequest);
//...
}
If the client.loginAsync doesn't return what you're looking for, then you'll need to approach this doing something similar to your current approach. Or if you are locked in to the event-based async pattern, you have other considerations - like whether or not you want to support cancellation, IsBusy, progress, incremental results and if you have the ability to have the event args inherit the System.ComponentModel.AsyncCompletedEventArgs, etc...
One final consideration, if the client.loginAsync is Task returning, even if it doesn't return the loginResponse you need to await it like so:
public async Task<loginResponse> Login(loginRequest request)
{
var tcs = new TaskCompletionSource<loginResponse>();
ClientGenerator.WebServiceUrl = _webServiceUrl;
ClientGenerator.InitializeService();
client = ClientGenerator.ServiceClient;
client.loginCompleted += (sender, loginResult) =>
{
if (loginResult.Error != null)
tcs.SetException(loginResult.Error);
else
tcs.TrySetResult(loginResult.Result);
};
await client.loginAsync(request);
return tcs.Task;
}
Update
After discussion with OP this .NET Fiddle seemed to align with his needs.
Change var loginResult = await Task.Run(() =>service.Login(loginRequest));
To var loginResult = await service.Login(loginRequest);
I have a old data access library that I need to use in my ASP.NET MVC application but I'm having trouble bringing it into the MVC world where many server-side operations are expected to be asynchronous. My data access library looks a like this:
public class MyOldDAL
{
public TaskResult CreateUser(string userName)
{
try
{
// Do user creation
return new TaskResult { Success = true, Message = "success" };
}
catch (Exception ex)
{
return new TaskResult { Success = false, Message = ex.Message };
}
}
}
And is called by my MVC application like this:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public async Task CreateAsync(ApplicationUser user)
{
await Task.Run(() => _dal.CreateUser(user.UserName));
}
}
This is fine until the method in Task.Run() fails for some reason. I'd like to be able to return TaskStatus.Faulted if TaskResult.Success is false but I can't find an example online on how to do it properly - all my Google searches turn up is how to use Task<T> which the IUserStore interface doesn't permit.
For the record, much as I'd like to I can't change MyOldDAL - it's targeting .NET 3.5 so no async or await there!
The normal way to report errors from tasks is via exceptions, so you'll just need to do that transformation yourself:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public Task CreateAsync(ApplicationUser user)
{
var result = _dal.CreateUser(user.UserName);
if (result.Success)
return Task.CompletedTask;
return Task.FromException(new InvalidOperationException(result.Message));
}
}
Note that Task.Run should not be used on ASP.NET.
Note: As Stephen Cleary noticed in his answer, Task.Run should not be used on ASP.NET.
Original answer (before comments):
Your CreateAsync method should normally be like this:
public async Task<TaskResult> CreateAsync(ApplicationUser user)
{
return await Task.Run(() => _dal.CreateUser(user.UserName));
}
But if you can't return Task<TaskResult> from CreateAsync method... well, than you can't obtain TaskResult from CreateAsync by definition. In that case you can store result locally:
private TaskResult taskResult;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.taskResult = result;
// process taskResult wherether you need
}
Or raise event with TaskResult payload, allowing client of MyUserStore to subscribe to this event:
public event EventHandler<TaskResult> TaskCompleted;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.OnTaskCompleted(result);
}
private void OnTaskCompleted(TaskResult result)
{
this.TaskCompleted?.Invoke(this, result);
}
I have the following ServiceStack web service
public class RetreadServices : Service
{
public IRepository Repository { get; set; }
//http://stackoverflow.com/questions/18902135/servicestack-no-server-side-async-support
public async Task<ValidationResponse> Get(RetreadSsnInfoAsync request)
{
return await Task.Factory.StartNew(() =>
{
Tenant.SetTenant(request.TenantId);
return new ValidationResponse { Result = Repository.CannotRetreadSsn(request.Ssn) };
});
}
public async Task<ValidationResponse> Get(RetreadEmailInfoAsync request)
{
return await Task.Factory.StartNew(() =>
{
Tenant.SetTenant(request.TenantId);
return new ValidationResponse { Result = Repository.CannotRetreadEmail(request.Emails) };
});
}
//more methods constructed in the same fashion
}
then in a separate class in my application I have this method (based off of code from #StephenCleary)
private async Task<ValidationResponse[]> DoOperationsConcurrentlyAsync(string tenantId, string[] emails, string ssn)
{
Task<ValidationResponse>[] tasks =
{
ResolveService<RetreadServices>().Get(new RetreadEmailInfoAsync { TenantId = tenantId, Emails = emails }),
ResolveService<RetreadServices>().Get(new RetreadSsnInfoAsync { TenantId = tenantId, Ssn = ssn })
};
await Task.WhenAll(tasks);
ValidationResponse[] result = new ValidationResponse[tasks.Length];
return result;
}
and being invoked like this
synchronous code...
synchronous code...
var result = DoOperationsConcurrentlyAsync(request.TenantId, request.Emails, request.Ssn);
synchronous code...
synchronous code...
The goal of all of this is to wait while the tasks process in parallel and hopefully decrease my over-all time... but the DoOperationsConcurrentlyAsync needs to block, how do I do that? Then as a bonus, how do I pass the return values back from the individual tasks up and out of the method with the async modifier?
I hope I'm understanding this correctly. But don't you just need to add the await keyword?
synchronous code...
synchronous code...
var result = await DoOperationsConcurrentlyAsync(request.TenantId, request.Emails, request.Ssn);
synchronous code...
synchronous code...
EDIT: Bonus question: Again, I hope I'm understanding you correctly...
But I would replace this:
await Task.WhenAll(tasks);
ValidationResponse[] result = new ValidationResponse[tasks.Length];
return result;
...with simply this:
return await Task.WhenAll(tasks);
That should work the way you want it.