[英]Correctly implementing Tasks in a WPF application
I'm looking for some advice on getting to grips with Task
s in WPF and was wondering if anyone could have a look over my code and point out what I'm doing incorrectly? 我正在寻找有关WPF中
Task
的一些建议,并且想知道是否有人可以看一下我的代码并指出我在做什么错误?
Basically the application takes a postcode from the UI which is used to instantiate a Task
service which will get Longitude/Latitude which can be accessed via the instance for use in another service or the UI itself. 基本上,应用程序从UI提取邮政编码,用于实例化
Task
服务,该服务将获得经度/纬度,可以通过实例访问该经度以用于其他服务或UI本身。
I think I may have a race condition which I'm looking to correct as when ResultTextBlock.Text
is set it's a zero, but stepping through instantiation I see those values set. 我认为我可能想解决一个竞态条件,因为将
ResultTextBlock.Text
设置为零时,但是逐步进行实例化时,我看到那些值已设置。
Any advice on Task
implementation and wiring would be greatly appreciated. 任何有关
Task
实施和接线的建议将不胜感激。
class PostcodeService
{
string _result;
string _postcode;
HttpResponseMessage _response;
RootObject rootObject;
public double Latitude { get; private set; }
public double Longitude { get; private set; }
public PostcodeService(string postcode)
{
this._postcode = postcode;
rootObject = new RootObject();
}
public async Task<string> GetLongLatAsync()
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.postcodes.io/postcodes/" + this._postcode);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try
{
_response = await client.GetAsync(client.BaseAddress);
if(_response.IsSuccessStatusCode)
{
//cast result into model and then set long/lat properties which can then be used in the UI
_result = await _response.Content.ReadAsStringAsync();
rootObject = JsonConvert.DeserializeObject<RootObject>(_result);
Longitude = Double.Parse(rootObject.result.longitude.ToString());
Latitude = Double.Parse(rootObject.result.latitude.ToString());
}
}
catch(Exception ex)
{
ex.ToString();
}
}
TaskCompletionSource<string> tc = new TaskCompletionSource<string>(_result);
return tc.ToString();
}
}
private void PostcodeButton_Click(object sender, RoutedEventArgs e)
{
_clearStatus();
if (_validatePostcode())
{
Task T1 = Task.Factory.StartNew(async () =>
{
// get long lat from api
_postcode = new PostcodeService(PostcodeTextBox.Text);
await _postcode.GetLongLatAsync();
});
//Race condition?
ResultTextBlock.Text = _postcode.Latitude.ToString();
}
}
Event handlers allow async void to be used 事件处理程序允许使用异步void
Reference Async/Await - Best Practices in Asynchronous Programming 参考Async / Await-异步编程最佳实践
private async void PostcodeButton_Click(object sender, RoutedEventArgs e) {
_clearStatus();
if (_validatePostcode()) {
// get long lat from api
_postcode = new PostcodeService(PostcodeTextBox.Text);
await _postcode.GetLongLatAsync(); //Offload UI thread
//Back on UI thread
ResultTextBlock.Text = _postcode.Latitude.ToString();
}
}
Secondly to avoid thread exhaustion, create a single HttpClient
instead of creating and disposing it when you need to perform this action 其次,为避免线程耗尽,请在需要执行此操作时创建单个
HttpClient
而不是创建和处理它
Reference You're using HttpClient wrong 参考您使用的HttpClient错误
The design of the service should be refactored into separate concerns 服务的设计应重构为单独的关注点
public class Location {
public Location(double lat, double lon) {
Latitude = lat;
Longitude = lon;
}
public double Latitude { get; private set; }
public double Longitude { get; private set; }
}
public class PostcodeService {
private static Lazy<HttpClient> client;
static PostcodeService() {
client = new Lazy<HttpClient>(() => {
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://api.postcodes.io/postcodes/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
});
}
public async Task<Location> GetLongLatAsync(string postcode) {}
var response = await client.Value.GetAsync(postcode);
if(response.IsSuccessStatusCode) {
//cast result into model and then set long/lat properties which can then be used in the UI
var rootObject = await response.Content.ReadAsAsync<RootObject>();
var Longitude = Double.Parse(rootObject.result.longitude.ToString());
var Latitude = Double.Parse(rootObject.result.latitude.ToString());
var result = new Location(Latitude, Longitude);
return result;
}
return null;
}
}
The event handler can now be refactored to 现在可以将事件处理程序重构为
private async void PostcodeButton_Click(object sender, RoutedEventArgs e) {
_clearStatus();
if (_validatePostcode()) {
// get long lat from api
var service = new PostcodeService();
var location = await service.GetLongLatAsync(PostcodeTextBox.Text); //Offload UI thread
//Back on UI thread
if(location != null) {
ResultTextBlock.Text = location.Latitude.ToString();
} else {
//Some message here
}
}
}
Your GetLongLatAsync()
method should return a string
: 您的
GetLongLatAsync()
方法应返回一个string
:
public async Task<string> GetLongLatAsync()
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.postcodes.io/postcodes/" + this._postcode);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
_response = await client.GetAsync(client.BaseAddress);
string result = null;
if (_response.IsSuccessStatusCode)
{
//cast result into model and then set long/lat properties which can then be used in the UI
result = await _response.Content.ReadAsStringAsync();
rootObject = JsonConvert.DeserializeObject<RootObject>(_result);
Longitude = Double.Parse(rootObject.result.longitude.ToString());
Latitude = Double.Parse(rootObject.result.latitude.ToString());
}
return result;
}
}
...and you should simply await GetLongLatAsync()
in the UI: ...,您只需在UI中等待
GetLongLatAsync()
:
private async void PostcodeButton_Click(object sender, RoutedEventArgs e)
{
_clearStatus();
if (_validatePostcode())
{
// get long lat from api
_postcode = new PostcodeService(PostcodeTextBox.Text);
string result = await _postcode.GetLongLatAsync();
ResultTextBlock.Text = result.ToString();
}
}
You don't need to use a TaskCompletionSource
nor start a new Task
to call an async method. 您无需使用
TaskCompletionSource
或启动新的Task
来调用异步方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.