我需要从其中一个IP地址提供程序URL获取我的公共IP地址。问题是这些服务不可靠,所以我必须回退到不同的URL。为了获得最佳性能,我想同时向所有服务提供商发起WebRequest,并考虑首先回复的人的结果。
这是我写的代码。它工作得非常好。但是我使用了EventWaitHandle
。我只是想知道这是否是正确的方法,或者是否可以在不使用WaitHandle
的情况下执行相同操作(仅使用async / await)?
private static readonly string[] IpProviders = new string[] {
"http://ipinfo.io/ip", "http://canihazip.com/s",
"http://icanhazip.com", "http://bot.whatismyipaddress.com" };
private static string _publicIp = null;
public static string PublicIp
{
get
{
if (_publicIp == null)
{
_publicIp = FetchPublicIp();
}
return _publicIp;
}
}
private static string FetchPublicIp()
{
using (MyResetEvent manualEvent = new MyResetEvent())
{
foreach (string providerUrl in IpProviders)
{
FetchPublicIp(providerUrl).
ContinueWith(x => OnResult(x.Result, manualEvent));
}
int looped = 0;
do
{
manualEvent.WaitOne();
lock (manualEvent)
{
if (!string.IsNullOrWhiteSpace(manualEvent.Result))
{
return manualEvent.Result;
}
else
{
manualEvent.Reset();
}
looped = manualEvent.Count;
}
} while (looped < IpProviders.Length);
}
return null;
}
private static async Task<string> FetchPublicIp(string providerUrl)
{
string externalip;
try
{
externalip = await new WebClient().DownloadStringTaskAsync(providerUrl);
}
catch (WebException ex)
{
Debug.WriteLine(ex);
externalip = null;
}
if (!string.IsNullOrWhiteSpace(externalip))
{
System.Net.IPAddress ip;
if (System.Net.IPAddress.TryParse(externalip.Trim(), out ip))
{
return ip.ToString();
}
}
return null;
}
private static void OnResult(string s, MyResetEvent manualEvent)
{
try
{
lock (manualEvent)
{
if (manualEvent.Result == null)
{
manualEvent.Result = s;
}
manualEvent.Count++;
manualEvent.Set();
}
}
catch (ObjectDisposedException ex)
{
Debug.WriteLine(ex);
}
}
这是MyResetEvent类:
internal class MyResetEvent : EventWaitHandle
{
public MyResetEvent()
: base(false, EventResetMode.ManualReset)
{
}
public string Result { get; set; }
public int Count { get; set; }
}
答案 0 :(得分:4)
你过分夸大这种方式。 TPL可以帮助你,而不是打你!
async Task<string> TakeFirstResponse(string[] urls)
{
return await await Task.WhenAny(
urls.Select(async url =>
await new WebClient().DownloadStringTaskAsync(url)));
}
为什么双重等待? Task.WhenAny
按设计返回Task<Task<T>>
。
答案 1 :(得分:0)
@Bas的答案是正确的(你应该接受它实际上),但我想提供一个更简洁的替代方案,使用我的Flurl库:
async Task<string> TakeFirstResponse(string[] urls)
{
return await await Task.WhenAny(urls.Select(url => url.GetStringAsync()));
}
Flurl.Http由HttpClient
支持,WebClient
更新,generally preferable支持{{1}},所有其他条件相同。