I have this asynchronous request:
Pubnub pn = new Pubnub(publishKey, subscribeKey, secretKey, cipherKey, enableSSL);
pn.HereNow("testchannel", res => //doesn't return a Task
{ //response
}, err =>
{ //error response
});
The problem is that I don't know how to run it synchronously. Please help.
I'm not familiar with pubnub, but what you're trying to achieve should be as simple as this:
Pubnub pn = new Pubnub(publishKey, subscribeKey, secretKey, cipherKey, enableSSL);
var tcs = new TaskCompletionSource<PubnubResult>();
pn.HereNow("testchannel", res => //doesn't return a Task
{ //response
tcs.SetResult(res);
}, err =>
{ //error response
tcs.SetException(err);
});
// blocking wait here for the result or an error
var res = tcs.Task.Result;
// or: var res = tcs.Task.GetAwaiter().GetResult();
Note that doing asynchronous stuff synchronously is not recommend. You should look at using async/await, in which case you'd do:
var result = await tcs.Task;
I solved this issue using the #Noseratio ideia with a simple enhancement.
private Task<string> GetOnlineUsersAsync()
{
var tcs = new TaskCompletionSource<string>();
_pubnub.HereNow<string>(MainChannel,
res => tcs.SetResult(res),
err => tcs.SetException(new Exception(err.Message)));
return tcs.Task;
}
// using
var users = await GetOnlineUsersAsync();
Related
I have a list of items, and for each item, I need to execute series of tasks.
For accessing data layer, I am using the following code:
public async Task<ExampleResult> GetExampleResultAsync(Parameter parameter, CancellationToken cancellationToken = default(CancellationToken))
{
GetCustomerResult result = null;
OracleConnection connection = this.Database.GetOracleConnection();
bool needClose = false;
if (connection.State != ConnectionState.Open)
{
await connection.OpenAsync(cancellationToken);
needClose = true;
}
try
{
using (OracleCommand cmd = connection.CreateCommand())
{
.... do the work
}
}
finally
{
if (needClose)
connection.Close();
}
return result;
}
however, this leads to concurrency and thus I am getting
the connection was not closed error.
The possible workaround for this that I ended up is to change the way of connecting to the database. I am thinking to use new connection instance for every request and surround these instances with using statement.
This would lead me lots of work and I would like to get an idea about best practices in the field for handling concurrency issues.
EDIT:
my caller function is below for your reference
public async Task<DomainResult<IList<MbRiskDto>>> QueryAsync(Action<MbrAccountAutoMatcherQueryParameter> parameter,
CancellationToken cancellationToken = default(CancellationToken))
{
parameter(_parameter);
var nonDeclaredMbrAccounts = await _nonDeclaredMbrAccountsQuery.QueryAsync(param => param.TransactionDate = _parameter.TransactionDate, cancellationToken);
if (nonDeclaredMbrAccounts.IsFailed)
nonDeclaredMbrAccounts.Errors.ForEach(error => _errors.Add(error));
var taskList = new List<Task<MbRiskDto>>();
var throttler = new SemaphoreSlim(initialCount: 10);
foreach (var nonDeclaredAccount in nonDeclaredMbrAccounts.Result)
{
await throttler.WaitAsync(cancellationToken);
var account = (AccountDto) nonDeclaredAccount.Clone();
var firstAccountHolder = Convert.ToInt32(account.AccountHolders.FirstOrDefault());
var task = Task.Run(async () =>
{
MbRiskDto result;
try
{
result = new MbRiskDto
{
KimNo = await GetKimNo(firstAccountHolder),
HesNo = account.AccountNo,
FinCode = await GetFinanceCode(account, firstAccountHolder, cancellationToken),
Unvan = account.SMA.Substring(0, Math.Min(account.SMA.Length, 54))
};
}
finally
{
throttler.Release();
}
return result;
}, cancellationToken);
taskList.Add(task);
}
var taskResult = await Task.WhenAll(taskList);
return DomainResult<IList<MbRiskDto>>.Success(taskResult);
}
According to the comments I removed Task.Run() clause and replaced with the code below. Right now, I believe that my concurrency problems are solved.
Func<Task<MbRiskDto>> mbRiskTask = async () => new MbRiskDto
{
KimNo = await GetKimNo(firstAccountHolder, cancellationToken),
HesNo = account.AccountNo,
FinCode = await GetFinanceCode(account, firstAccountHolder, cancellationToken),
Unvan = account.SMA.Substring(0, Math.Min(account.SMA.Length, 54))
};
taskList.Add(mbRiskTask.Invoke());
Thus, Resulting code can be seen below
public async Task<DomainResult<IList<MbRiskDto>>> QueryAsync(Action<MbrAccountAutoMatcherQueryParameter> parameter,
CancellationToken cancellationToken = default(CancellationToken))
{
parameter(_parameter);
var nonDeclaredMbrAccounts = await _nonDeclaredMbrAccountsQuery.QueryAsync(param => param.TransactionDate = _parameter.TransactionDate, cancellationToken);
if (nonDeclaredMbrAccounts.IsFailed)
nonDeclaredMbrAccounts.Errors.ForEach(error => _errors.Add(error));
var taskList = new List<Task<MbRiskDto>>();
foreach (var nonDeclaredAccount in nonDeclaredMbrAccounts.Result)
{
var account = (AccountDto) nonDeclaredAccount.Clone();
var firstAccountHolder = Convert.ToInt32(account.AccountHolders.FirstOrDefault());
Func<Task<MbRiskDto>> mbRiskTask = async () => new MbRiskDto
{
KimNo = await GetKimNo(firstAccountHolder, cancellationToken),
HesNo = account.AccountNo,
FinCode = await GetFinanceCode(account, firstAccountHolder, cancellationToken),
Unvan = account.SMA.Substring(0, Math.Min(account.SMA.Length, 54))
};
//var task = Task.Run();
taskList.Add(mbRiskTask.Invoke());
}
var taskResult = await Task.WhenAll(taskList);
return DomainResult<IList<MbRiskDto>>.Success(taskResult);
}
EDIT:
With this refactoring, the operation takes approximately 4minutes to finish. Do you have any suggestion in order to make it work faster?
Let's say I have more Uri's. I need to validate, if they are reachable.
public RelayCommand TestConnectionCommand => new RelayCommand(async () =>
{
var res1 = await ValidateUriAsync(uri);
var res2 = await ValidateUriAsync(uri);
});
private async Task<bool> ValidateUriAsync(Uri uri)
{
try
{
var request = WebRequest.CreateHttp(uri);
var result = await request.GetResponseAsync();
return true;
}
catch (Exception e)
{
return false;
}
}
When the program comes to first await it takes some time to validate the uri, after I get the result, I can show the result on the View. Then program goes next and I validate second uri. I'd like to do that parallel, without awaiting. I was thinking about using Begin/EndGetResponse. I need to show the result for each validation on the View. Validation succeeded/failed.
Many thanks for advice.
When using await you stop the execution until the task returns, instead wait for all task to finish:
var task1 = ValidateUriAsync(uri);
var task2 = ValidateUriAsync(uri);
await Task.WhenAll(task1, task2);
or to wait until the first fault:
var tasks = new List<Task>
{
ValidateUriAsync(), ValidateUriAsync(uri)
};
while (tasks.Any())
{
var t = await Task.WhenAny(tasks);
if (t.IsFaulted)
{
//Faulty
break;
}
tasks.Remove(t);
}
My code is an async api call, and looks like the example below
public async Task<IEnumerable<TaskObject>> GetTaskObjects()
{
var tasks = new List<Task<TaskObject>>();
var shizzle = Task.Run(() => { Thread.Sleep(2000); return new TaskObject("1"); });
var shizzle2 = Task.Run(() => { Thread.Sleep(1000); return new TaskObject("2"); });
tasks.Add(shizzle.ContinueWith(part1 => { Thread.Sleep(1000); return part1.Result; }));
tasks.Add(shizzle2.ContinueWith(part1 => { Thread.Sleep(1000); return part1.Result; }));
await Task.WhenAll(tasks);
return tasks.Select(x => x.Result).ToList();
}
The controller is a Stateless Service Fiber Web Api that makes some calls to a statefull service. Is this a good solution? Are there beter ones? Are the async and await keywords even necessary if this is an api call?
public async Task<IEnumerable<TaskObject>> GetTaskObjects2()
{
var tasks = new List<Task<TaskObject>>();
var shizzle = Task.Run(() => { Thread.Sleep(2000); return new TaskObject("1"); });
var shizzle2 = Task.Run(() => { Thread.Sleep(1000); return new TaskObject("2"); });
//Add your task to the collection
tasks.Add(shizzle);
tasks.Add(shizzle2);
//wait for when all task are finished and it will return the data.
return await Task.WhenAll(tasks);
}
If this line of codes are really awaitable
/// At this point, all two tasks are running at the same time.
var shizzle = DoShizzleAsync();
var shizzle2 = DoShizzle2Async();
await Task.WhenAll(shizzle2, shizzle);
See Stephen Cleary blog for more information
I have to have a breakpoint on the indicated line below for the following code to work. Otherwise, the program just pauses indefinitely.
async Task<List<PingReply>> PingAsync()
{
var pingTargetHosts = GetIPs();
var pingTasks = pingTargetHosts.Select(host => new Ping().SendPingAsync(host, 2000)).ToList();
var pingResults = await Task.WhenAll(pingTasks); //THIS LINE NEEDS A BREAKPOINT TO WORK
return pingResults.ToList();
}
The code is called like this
List<PingReply> GetReplies()
{
var PingIPs = PingAsync();
MessageBox.Show("Loading:...");
List<PingReply> Results = PingIPs.Result;
return Results;
}
Could anyone tell me how I need to amend my code in order to remove the breakpoint but still have a functional piece of code.
EDIT:
Not tested, but 99% sure this will work.
async Task<List<PingReply>> PingAsync()
{
var pingTargetHosts = GetIPs();
var pingTasks = pingTargetHosts.Select(async host => await new Ping().SendPingAsync(host, 2000)).ToList();
var pingResults = await Task.WhenAll(pingTasks);
return pingResults.ToList();
}
async Task<List<PingReply>> GetReplies()
{
var PingIPs = PingAsync();
MessageBox.Show("Loading:...");
return await PingIPs;
}
async Task BuildDictionary()
{
List<PingReply> Replies = await GetReplies();
//Use this list via foreach
}
async private void button1_Click(object sender, EventArgs e)
{
EthernetCheck checker = new EthernetCheck();
checker.Check();
bool IsEthernetIn = checker.PluggedIn;
if (IsEthernetIn)
{
await BuildDictionary();
//Do Stuff
}
}
Your code is deadlocking because you're blocking on asynchronous code. To fix it, use async all the way:
async Task<List<PingReply>> GetRepliesAsync()
{
var PingIPs = PingAsync();
MessageBox.Show("Loading:...");
return await PingIPs;
}
Usage:
var replies = await GetRepliesAsync();
When use async/await you should remember that .net framework will control the flow of program execution so, I recommend u to make all calls asynchronous to avoid this kind of problem.
async Task<List<PingReply>> PingAsync()
{
var pingTargetHosts = await GetIPs();
var pingTasks = pingTargetHosts.Select(host => await new Ping().SendPingAsync(host, 2000)).ToList();
var pingResults = await Task.WhenAll(pingTasks);
return pingResults.ToList();
}
Recently started with ReactiveUI and so far so good. While trying to figure out stuff I came across a HttpClient / Refit problem. I have a long list and every item in that list have to make a request to a API as follows.
This is a ReactiveObject
public SearchResult()
{
LoadStats = ReactiveCommand.CreateAsyncTask(_ => DownloadMultipleCalls(this.IdText, this.DisplayText));
LoadStats
.ThrownExceptions
.SubscribeOn(RxApp.MainThreadScheduler)
.Subscribe(ex => UserError.Throw("Couldn't load stats", ex));
_avaText = LoadStats.Select(x => x.Ava).ToProperty(this, x => x.AvaText, string.Empty);
_perText = LoadStats.Select(x => x.Per).ToProperty(this, x => x.PerText, string.Empty);
this.WhenAnyValue(x => x.IdText)
.Where(x => !string.IsNullOrWhiteSpace(x))
//.Select(_ => Observable.Timer(DateTimeOffset.MinValue, TimeSpan.FromSeconds(1), RxApp.MainThreadScheduler))
.Retry(5)
.InvokeCommand(LoadStats);
}
In DownloadMultipleCalls the following is happening.
async static Task<AvaPer> DownloadMultipleCalls(string id, string displaytext)
{
AvaPer response = new AvaPer();
var block = new ActionBlock<string>(async service =>
{
response = await DownloadCall(service, displaytext);
},
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 4 });
block.Post(id);
block.Complete();
await block.Completion;
return response;
}
async static Task<AvaPer> DownloadCall(string id, string displaytext)
{
System.Diagnostics.Debug.WriteLine($"Starting download at {DateTimeOffset.Now}");
var client = new HttpClient(NetCache.UserInitiated)
{
BaseAddress = new Uri("https://api.adres.com"),
};
//client.Timeout = TimeSpan.FromMilliseconds(10);
var token = GetToken;
client.DefaultRequestHeaders.Add("authToken", token);
var api = RestService.For<IMyApi>(client);
AvaPer idEntries = new AvaPer();
try
{
var searchResult = await api.GetAvaPerStatusRaw(id); // <== Here is the error (400 - Bad Request)
//var parsedEntries = Task.Run(() => {
idEntries = new AvaPer
{
Id = searchResult.Id.ToString() ?? string.Empty,
display = displaytext ?? string.Empty,
Ava = searchResult.AvailabilityStatus ?? string.Empty,
Per = searchResult.PerformanceStatus ?? string.Empty,
Updated = DateTimeOffset.Now.ToLocalTime().ToString() ?? string.Empty
};
//}).ConfigureAwait(false);
//return idEntries;
}
catch (ApiException ex)
{
var statusCode = ex.StatusCode;
var error = ex.GetContentAs<Models.ServiceModel.ErrorResponse>();
}
return idEntries;
}
Finally it goes all wrong with this line of code var searchResult = await api.GetAvaPerStatusRaw(id); The app crashes with a response "400 Bad Request" and if I look into the error it sais that the there is no id value inplace of the GET request. But when I check the initial value of id there is a value so it should not respond with that. Can anyone help me with this? If you have any question, please do not hesitate to ask. Thank you very much.
Kind regards,
Fernando