开发者

Lock used in Cache Item callback and other method doesn't seem to lock

Simplest explanation I can produce:

In my .NET1.1 web app I create a file on disc, in the Render method, and add an item to the Cache to expire within, say, a minute. I also have a callback method, to be called when the cache item expires, which deletes the file created by Render. In the Page_Init method I try to access the file which the Render method wrote to disc. Both these methods have a lock statement, locking a private static Object.

Intention:

To create a page which essentially writes a copy of itself to disc, which gets deleted before it gets too old (or out of date, content-wise), while serving the file if it exists on disc.

Problem observed:

This is really two issues, I think. Requesting the page does what I expect, it renders the page to disc and serves it immediately, while adding the expiry item to the cache. For testing the expiry time is 1 minute.

I then expect that the callback method will get called after 60 seconds and delete the file. It doesn't.

After another minute (for the sake of argument) I refresh the page in the开发者_开发问答 browser. Then I can see the callback method get called and place a lock on the lock object. The Page_Init also gets called and places a lock on the same object. However, both methods appear to enter their lock code block and proceed with execution.

This results in: Render checks file is there, callback method deletes file, render method tries to serve now-deleted-file.

Horribly simplified code extract:

public class MyPage : Page
{
  private static Object lockObject = new Obect();

  protected void Page_Init(...)
  {
    if (File.Exists(...))
    {
      lock (lockObject)
      {
        if (File.Exists(...))
        {
          Server.Transfer(...);
        }
      }
    }
  }

  protected override void Render(...)
  {
    If (!File.Exists(...))
    {
      // write file out and serve initial copy from memory
      Cache.Add(..., new CacheItemRemovedCallback(DoCacheItemRemovedCallback));
    }
  }

  private static void DoCacheItemRemovedCallback(...)
  {
    lock (lockObject)
    {
      If (File.Exists(...))
        File.Delete(...);
    }
  }
}

Can anyone explain this, please? I understand that the callback method is, essentially, lazy and therefore only calls back once I make a request, but surely the threading in .NET1.1 is good enough not to let two lock() blocks enter simultaneously?

Thanks,

Matt.


Not sure why your solution doesn't work, but that might be a good thing, considering the consequences...

I would suggest a completely different route. Separate the process of managing the file from the process of requesting the file.

Requests should just go to the cache, get the full path of the file, and send it to the client.

Another process (not bound to requests) is responsible for creating and updating the file. It simply creates the file on first use/access and stores the full path in the cache (set to never expire). At regular/appropriate intervals, it re-creates the file with a different, random name, sets this new path in the cache, and then deletes the old file (being careful that it isn't locked by another request).

You can spawn this file managing process on application startup using a thread or the ThreadPool. Linking your file management and requests will always cause you problems as your process will be run concurrently, requiring you to do some thread synchronization which is always best to avoid.


First thing I would do is open the Threads window and observe which thread is the Page_Init is running on and which thread the Call Back is running on. The only way I know that two methods can place a lock on the same object is if they are running in the same thread.

Edit

The real issue here is how Server.Transfer actually works. Server.Transfer simply configures some ASP.NET internal details indicating that the request is about to be transfer to a different URL on the server. It then calls Response.End which in turn throws a ThreadAbortException. No actual data has been read or sent to the client at that time.

Now when the exception occurs code execution leaves the block of code protect by the lock. At this time the Call back function can acquire the lock and delete the file.

Now somewhere deep inside ASP.NET the ThreadAbortException is handled in some way and the request for the new URL is processed. At this time it finds the file has gone missing.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜