我正在尝试实现一个简单的Windows 8 C#XAML应用程序,其中有两个调用来访问单个Web服务,一个是从项目加载和显示数据,另一个是显示通知。 由于对同一个Web服务进行了两次调用,我希望如果已经对该服务进行了一次调用,则另一个调用应该等待,并在第一次调用时使用相同的响应。
我如何实现这种功能?我没有添加任何代码,因为我没有为此编写的代码。我只想先思考然后再编码。
请告诉我,我可以为这种项目结构寻求帮助吗?
答案 0 :(得分:1)
您可以通过缓存当前正在下载的Task
来执行此操作,如果存在缓存的Task
,则不会再次开始下载:
private volatile Task<string> m_cachedWebServiceTask;
async Task<string> AccessWebServiceAsync()
{
var task = m_cachedWebServiceTask;
if (task == null)
task = m_cachedWebServiceTask = DownloadFromWebServiceAsync();
try
{
return await task;
}
finally
{
m_cachedWebServiceTask = null;
}
}
请注意,此代码具有竞争条件:如果您同时拨打AccessWebServiceAsync()
两次,则DownloadFromWebServiceAsync()
将被调用两次的可能性很小。但由于这仅仅是一次优化,我认为这应该不是问题。如果您遇到问题,则需要通过锁定来保护对该字段的访问。
答案 1 :(得分:0)
由于我觉得这个问题需要进一步关注,而且它的解决方案仍然可以优化,我决定发布另一种方法。 OP主要是关于利用以下3个要求范围的问题:应用程序中的用户体验,应用程序的内部要求以及 Web服务的加载有多个请求。
因此,管理非常短的时间内发生的事情它实际上是解决问题的方法。
在客户端,Service1Client
类:
public partial class Service1Client
{
// default time buffer value
int _timeBuffer = 100;
// a time buffer used for returning the same response
public int TimeBuffer
{
get { return _timeBuffer; }
set { _timeBuffer = value; }
}
// the start and end time of the web service request
static DateTime _start, _end;
// a volatile static variable to store the response in the buffering time
volatile static string _response;
// used for blocking other threads until the current thread finishes it's job
object _locker = new object();
public async Task<string> GetResponseData()
{
return await Task.Factory.StartNew<string>(() =>
{
lock (_locker)
{
if (DateTime.Now >= _end.AddMilliseconds(TimeBuffer))
{
_start = DateTime.Now;
var async = GetDataAsync();
_response = async.Result;
_end = DateTime.Now;
}
}
return _response;
});
}
}
用于测试的控制台应用程序:
class Program
{
static void Main(string[] args)
{
while (true)
{
var client = new ServiceReference1.Service1Client();
client.TimeBuffer = 150;
Console.WriteLine(client.GetResponseData().Result);
if (Console.ReadKey().Key == ConsoleKey.Enter)
break;
}
}
}
作为评论,请注意,出于明确样本的原因,我决定将GetDate
WCF服务方法的返回类型从DateTime
更改为string
。
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData();
}
public class Service1 : IService1
{
public string GetData()
{
System.Threading.Thread.Sleep(5000);
return DateTime.Now.ToString();
}
}
答案 2 :(得分:-1)
对于您的场景,一个可行的想法是扩展服务类。
IService1
接口定义:
[ServiceContract]
public interface IService1
{
[OperationContract]
DateTime GetData();
}
Service1
类定义:
public class Service1 : IService1
{
public DateTime GetData()
{
System.Threading.Thread.Sleep(5000);
return DateTime.Now;
}
}
在客户端,扩展Service1Client
类定义并添加新方法:
public partial class Service1Client
{
static bool _isOpen;
static DateTime? _cachedResponse;
object _locker = new object();
public DateTime GetResponseData()
{
if (!_isOpen)
{
if (!_cachedResponse.HasValue)
{
lock (_locker)
{
_isOpen = true;
_cachedResponse = GetData();
_isOpen = false;
}
return _cachedResponse.Value;
}
else
{
Task.Factory.StartNew<DateTime>(() =>
{
lock (_locker)
{
_isOpen = true;
_cachedResponse = GetData();
_isOpen = false;
}
return _cachedResponse.Value;
});
}
}
return _cachedResponse.Value;
}
}
测试它:
class Program
{
static void Main(string[] args)
{
while (true)
{
var client = new ServiceReference1.Service1Client();
Console.WriteLine(client.GetResponseData());
if (Console.ReadKey().Key == ConsoleKey.Enter)
break;
}
}
}