admin管理员组文章数量:1122846
In my WPF application (.NET 8.0) I want to consume a asynchronous operation from a 3rd party library. When testing with all code inside the MainWindow.xaml.cs it works smoothely. When I move the same code into a separate class, using a static constructor, the operation is blocking indefinitely even with WaitAsync.
Here is my code:
public class VirtuosoCommunication
{
..
public static VirtuosoCommunication? Create(...)
{
VirtuosoCommunication vc = new VirtuosoCommunication();
// setting private variables for vc
if (!vc.TestRead(log)) return null;
if (!vc.TestWrite(log)) return null;
return vc;
}
private bool TestRead(Log? log)
{
string testGraph = "...";
Task<Graph?> task = LoadGraphFromSparqlRemoteEndpoint(testGraph, log);
Graph? g = task.Result;
return g != null;
}
internal async Task<Graph?> LoadGraphFromSparqlRemoteEndpoint(string graphFullName, Log? log)
{
Uri baseUri = new Uri(_readEndpoint ?? "");
HttpClient client = new HttpClient();
client.BaseAddress = baseUri;
SparqlQueryClient sparqlQueryClient = new SparqlQueryClient(client, baseUri);
Graph result = new Graph();
IGraph? tmp = null;
try
{
while (tmp == null || tmp.Triples.Count == limit)
{
string query = "construct { ?s ?p ?o } FROM <" + graphFullName + "> where {?s ?p ?o.} OFFSET " + offSet.ToString() + " LIMIT " + limit.ToString();
Task<IGraph> task = sparqlQueryClient.QueryWithResultGraphAsync(query);
tmp = await task.WaitAsync(TimeSpan.FromSeconds(4));
...
}
}
catch (Exception ex) { ... }
return result;
}
When I run the code line by line, it stops executing at tmp = await task.WaitAsync. When I then hit the "Break execution" Button in Visual Studio, it is showing me the line Graph? g = task.Result
in the TestRead method to be the next to be executed when the thread returns.
As I said, the same async call in a test routine completely placed in MainWindow.xaml.cs works without problem, so I guess I am somehow using the async call wrongly.
UPDATE 25.11.2024
From the answer of this question I changed the TestRead
method to:
private bool TestRead(Log? log)
{
string testGraph = "...";
//Task<Graph?> task = LoadGraphFromSparqlRemoteEndpoint(testGraph, log);
Task<Graph?> task = Task.Run<Graph?>(async() => await LoadGraphFromSparqlRemoteEndpoint(testGraph, log));
Graph? g = task.Result;
return g != null;
}
It works this way (yeah!), but I am not sure if this is the correct way to do it. Regarding parallelization, I just need the call to the SPARQL endpoint to not block my UI, I don't need to run other, parallel tasks meanwhile.
In my WPF application (.NET 8.0) I want to consume a asynchronous operation from a 3rd party library. When testing with all code inside the MainWindow.xaml.cs it works smoothely. When I move the same code into a separate class, using a static constructor, the operation is blocking indefinitely even with WaitAsync.
Here is my code:
public class VirtuosoCommunication
{
..
public static VirtuosoCommunication? Create(...)
{
VirtuosoCommunication vc = new VirtuosoCommunication();
// setting private variables for vc
if (!vc.TestRead(log)) return null;
if (!vc.TestWrite(log)) return null;
return vc;
}
private bool TestRead(Log? log)
{
string testGraph = "...";
Task<Graph?> task = LoadGraphFromSparqlRemoteEndpoint(testGraph, log);
Graph? g = task.Result;
return g != null;
}
internal async Task<Graph?> LoadGraphFromSparqlRemoteEndpoint(string graphFullName, Log? log)
{
Uri baseUri = new Uri(_readEndpoint ?? "");
HttpClient client = new HttpClient();
client.BaseAddress = baseUri;
SparqlQueryClient sparqlQueryClient = new SparqlQueryClient(client, baseUri);
Graph result = new Graph();
IGraph? tmp = null;
try
{
while (tmp == null || tmp.Triples.Count == limit)
{
string query = "construct { ?s ?p ?o } FROM <" + graphFullName + "> where {?s ?p ?o.} OFFSET " + offSet.ToString() + " LIMIT " + limit.ToString();
Task<IGraph> task = sparqlQueryClient.QueryWithResultGraphAsync(query);
tmp = await task.WaitAsync(TimeSpan.FromSeconds(4));
...
}
}
catch (Exception ex) { ... }
return result;
}
When I run the code line by line, it stops executing at tmp = await task.WaitAsync. When I then hit the "Break execution" Button in Visual Studio, it is showing me the line Graph? g = task.Result
in the TestRead method to be the next to be executed when the thread returns.
As I said, the same async call in a test routine completely placed in MainWindow.xaml.cs works without problem, so I guess I am somehow using the async call wrongly.
UPDATE 25.11.2024
From the answer of this question I changed the TestRead
method to:
private bool TestRead(Log? log)
{
string testGraph = "...";
//Task<Graph?> task = LoadGraphFromSparqlRemoteEndpoint(testGraph, log);
Task<Graph?> task = Task.Run<Graph?>(async() => await LoadGraphFromSparqlRemoteEndpoint(testGraph, log));
Graph? g = task.Result;
return g != null;
}
It works this way (yeah!), but I am not sure if this is the correct way to do it. Regarding parallelization, I just need the call to the SPARQL endpoint to not block my UI, I don't need to run other, parallel tasks meanwhile.
Share Improve this question edited Nov 25, 2024 at 11:20 Aaginor asked Nov 22, 2024 at 11:30 AaginorAaginor 4,78212 gold badges55 silver badges77 bronze badges 12 | Show 7 more comments2 Answers
Reset to default 4As Fildor said in the comments, you're not using a static cctor
; you're using a static factory method.
And that's a good thing. A ctor
or cctor
cannot be async
, so you wouldn't be able to await
anything. But a factory method can be async
. And in this case, it should be:
public static async Task<VirtuosoCommunication?> Create(...)
{
VirtuosoCommunication vc = new VirtuosoCommunication();
// setting private variables for vc
if (!await vc.TestRead(log)) return null;
if (!await vc.TestWrite(log)) return null;
return vc;
}
private async Task<bool> TestRead(Log? log)
{
string testGraph = "...";
Graph? g = await LoadGraphFromSparqlRemoteEndpoint(testGraph, log);
return g != null;
}
Next, I'd suggest looking at your HttpClient
usage, which is problematic:
- You're using HttpClient wrong and it is destabilizing your software | ASP.NET Monsters
- You're (probably still) using HttpClient wrong and it is destabilizing your software
You should consider using IHttpClientFactory
instead of creating a new HttpClient
instance on every call.
Use IHttpClientFactory to implement resilient HTTP requests - .NET | Microsoft Learn
Your updated answer using Task.Run()
is likely correct, but it is not completely clear without context:
When you're awaiting tasks with await DoSomething();
the current synchronization context is captured (if called from a button click in WPF, this will be the main thread). When the async operation completes, the state machine will schedule the next line of code to be executed in the same context (ie. the main thread). However, since your calling task.Result
in TestRead
you're already blocking the main thread, resulting in a deadlock.
Calling Task.Run()
avoids this problem by making sure everything "inside" the Task.Run block is executed on the thread pool, which does not preserve synchronization context and will not deadlock as it will just spawn new threads if all of them are blocked.
Please note that it would still be better to use async / await all the way through and avoid Task.Result
and Task.Wait()
as much as possible. If you can change the TestRead
function to return a Task instead (like Richard Deeming proposed here https://stackoverflow.com/a/79214898/1074014) definitely go for this approach!
本文标签: casync await blocks execution even with taskWaitAsyncStack Overflow
版权声明:本文标题:c# - async await blocks execution even with task.WaitAsync - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736304081a1932109.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
QueryWithResultGraphAsync
accepts a cancellation token, why don't you just use that for timeouts? – Charlieface Commented Nov 22, 2024 at 12:38