【问题标题】:Locking with asynchronous httpwebrequest使用异步 httpwebrequest 锁定
【发布时间】:2011-03-14 23:04:44
【问题描述】:

我有一个对象,它从服务器下载文件,将其异步保存到独立存储中,并提供 GetData 方法来检索数据。我会用一个

IsolatedStorageFile storageObj; //initialized in the constructor

lock(storageObj)
{
   //save code
}

在响应和

lock(storageObj)
{
   //load code
}

在 GetData 方法中?

编辑:我将在这里提供一些背景信息。 该应用程序(适用于 Windows Phone)需要从服务器下载和缓存多个文件,因此我创建了一个类型,它采用 2 个字符串(一个 uri 和一个文件名),从给定的 uri 发送数据并保存它。同样的对象也有get data方法。这是代码(简化了一点)

public class ServerData: INotifyPropertyChanged
{
    public readonly string ServerUri;
    public readonly string Filename;
    IsolatedStorageFile appStorage;

    DownloadState _downloadStatus = DownloadState.NotStarted;
    public DownloadState DownloadStatus
    {
        protected set
        {
            if (_downloadStatus == value) return;
            _downloadStatus = value;
            OnPropertyChanged(new PropertyChangedEventArgs("DownloadStatus"));
        }
        get { return _downloadStatus; }
    }

    public ServerData(string serverUri, string filename)
    {
        ServerUri = serverUri;
        Filename = filename;
        appStorage = IsolatedStorageFile.GetUserStoreForApplication();
    }
    protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, args);
    }

    public void RequestDataFromServer()
    {
        DownloadStatus = DownloadState.Downloading;
   //this first bit adds a random unused query to the Uri, 
   //so Silverlight won't cache the request
        Random rand = new Random();
        StringBuilder uriText = new StringBuilder(ServerUri);
        uriText.AppendFormat("?YouHaveGotToBeKiddingMeHack={0}", 
                             rand.Next().ToString());
        Uri uri = new Uri(uriText.ToString(), UriKind.Absolute);
        HttpWebRequest serverRequest = (HttpWebRequest)WebRequest.Create(uri);
        ServerRequestUpdateState serverState = new ServerRequestUpdateState();
        serverState.AsyncRequest = serverRequest;
        serverRequest.BeginGetResponse(new AsyncCallback(RequestResponse),
            serverState);
    }

    void RequestResponse(IAsyncResult asyncResult)
    {
        var serverState = (ServerRequestUpdateState)asyncResult.AsyncState;
        var serverRequest = (HttpWebRequest)serverState.AsyncRequest;
        Stream serverStream;
        try
        {
            // end the async request
            serverState.AsyncResponse = 
                (HttpWebResponse)serverRequest.EndGetResponse(asyncResult);
            serverStream = serverState.AsyncResponse.GetResponseStream();
            Save(serverStream);
            serverStream.Dispose();
        }
        catch (WebException)
        {
            DownloadStatus = DownloadState.Error;
        }

        Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            DownloadStatus = DownloadState.FileReady;
        });
    }

    void Save(Stream streamToSave)
    {
        StreamReader reader = null;
        IsolatedStorageFileStream file;
        StreamWriter writer = null;
        reader = new StreamReader(streamToSave);

        lock (appStorage)
        {
            file = appStorage.OpenFile(Filename, FileMode.Create);
            writer = new StreamWriter(file);
            writer.Write(reader.ReadToEnd());
            reader.Dispose();
            writer.Dispose();
        }
    }

    public XDocument GetData()
    {
        XDocument xml = null;
        lock(appStorage)
        {
            if (appStorage.FileExists(Filename))
            {
                var file = appStorage.OpenFile(Filename, FileMode.Open);
                xml = XDocument.Load(file);
                file.Dispose();
            }
        }
        if (xml != null)
            return xml;
        else return new XDocument();
    }
}

【问题讨论】:

  • 如果是这种情况,为什么不同步呢?
  • 那会阻塞 ui 线程

标签: c# asynchronous locking


【解决方案1】:

您的问题并没有提供大量的背景信息,而且提供的信息量很大,人们可能倾向于简单地告诉您是的,也许只是少量但相关的补充。

实践通常会看到锁定发生在专用object 的实例上,确保不要锁定this,因为您锁定了当前对象的整个实例,这几乎不是意图 -但是,在您的情况下,我们并不完全正确地知道,但是,我几乎不认为锁定您的存储实例是要走的路。

此外,由于您提到了客户端和服务器的交互,它并不那么简单。

根据负载和许多其他因素,您可能希望从服务器提供多次文件读取,但在正在下载的客户端上任何时候只提供一次写入;为此,我建议使用ReaderWriterLockSlim 类,它公开了TryEnterReadLockTryEnterWriteLock 和相应的发布方法。

有关此类的更多详细信息,请参阅this MSDN link

另外,在锁定范围内编码时,请记住使用trycatchfinally,始终在finally 块中释放锁定。

【讨论】:

  • 感谢您的建议,我将扩展我的问题。
  • 哦,在我忘记之前,这个应用程序中没有写入服务器,请求数据的参数严格在 uri 中。
  • 很遗憾,ReaderWriterLockSlim 不在 Silverlight 中。
  • @Chris:在这种情况下看看Monitor - msdn.microsoft.com/en-us/library/…
  • 好的,那么 Monitor.Enter(Object) 和 Monitor.TryEnter(Object) 的区别是 TryEnter 不会阻塞?唔。您的意思是,如果我有多个线程试图同时读取文件,并且它们使用 lock(Object) 语句相互阻塞。现在的问题是如何处理 TryEnter() == false 的情况。嗯……
【解决方案2】:

什么类包含这段代码?这很重要,因为如果它被多次创建,这很重要。如果它在进程的生命周期内被创建一次,你可以这样做,否则你应该锁定一个 static 对象实例。

我相信虽然创建一个仅用于锁定目的的单独对象是一种很好的做法,但我忘记了原因。例如:

IsolatedStorageFile storageObj; //initialized in the constructor
(static) storageObjLock = new object();
...
// in some method
lock(storageObjLock)
{
   //save code 
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-30
    • 2010-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    相关资源
    最近更新 更多