2

I have simple page, that loads XML from filesystem, fills textboxes, these can be updated and saved. For serializing and deserializing I am using these methods:

private static readonly object FormDataLock = new object();

public static FormData getFormData(string filename)
{
    FormData fd;
    lock (FormDataLock)
    {
        XmlSerializer x = new XmlSerializer(typeof(FormData));
        using (Stream s = new FileStream(filename, FileMode.Open, FileAccess.Read))
        {
            return (FormData)x.Deserialize(s);
        }
    }
}

public void saveFormData(string filename)
{
    lock (FormDataLock)
    {
        XmlSerializer x = new XmlSerializer(typeof(FormData));
        using (Stream s = new FileStream(filename, FileMode.Create, FileAccess.Write))
        {
            x.Serialize(s, this);
        }
    }
}

But the problem is, that I am gettig sometimes (as I have notticed when I click the "save" button too fast after PageLoad) the IOException:

IOException: The process cannot access the file ".." because it is being used by another process. 

I was trying to lock the block with mutex, but it is still not working properly. The page form is quite simple, but I am using UpdatePanel on it (is it important?).

When the page is loaded and first save request was done OK, I can click the button as fast as I can and everything is OK (no exception).

Baboon
  • 75
  • 1
  • 7

2 Answers2

2

XmlSerialization creates new dll's on the fly which are specific to the class you're trying to serialise in the temp directory. These are created to increase performance. See http://msdn.microsoft.com/en-us/library/swxzdhc0.aspx

Instead of calling the GC.Collect etc... try creating the serializer as a static field on your class. This should improve performance and might solve your problem as it's only ever going to be created once.

This code will create a single xmlserializer in a thread safe way. Do NOT add a [ThreadStatic] attribute to this as this will ensure the code gets executed once per thread and make it thread unsafe again!

private static readonly XmlSerializer xmlSerializer = 
    new XmlSerializer(typeof(FormData));
AndyD
  • 5,252
  • 35
  • 32
  • As I wrote in previous post - I found this solution here: http://www.theroks.com/using-the-xmlserializer-in-multithreaded-applications/, I have also try it, but I think that it didnt solve the problem. – Baboon Dec 17 '12 at 09:05
  • So I have try it again (I haven´t used [ThreadStatic] before) and it seems to be OK - problem was that when I was trying it before I maybe broke it so propely that after new compilation of the solution with the singleton has still file locked from previous tests. – Baboon Dec 17 '12 at 13:50
  • so no need for GC.Collect? – AndyD Dec 17 '12 at 13:57
  • I have try this solution for some time, problems are gone, and it seems to be better than GC.Collect, because it is faster. – Baboon Jan 17 '13 at 17:02
0

I had similar problem and I hope this will help you too. The problem was that garbage collector didn't clean up before your second click, so you should try to call it manually. Try to call GC before living using

GC.Collect();
GC.WaitForPendingFinalizers();
VladL
  • 12,769
  • 10
  • 63
  • 83
  • Please don't mess with GC, unless you absolutely know what you are doing. If you have to force GC, most of the time, it's the indicator of problems in your code. – Evgeny Lukashevich Dec 16 '12 at 23:51
  • Nice and fast :) I should probably never find this solution. So it seems to work well. If problem will appears again I will send you feedback. Thank you. – Baboon Dec 16 '12 at 23:56
  • Eugene - I was searching about any possible problem with the code I have posted but I had no luck. Only one clue I have found was here: http://www.theroks.com/using-the-xmlserializer-in-multithreaded-applications/ (there is some infos about possible leak of XMLSerializer), but after implementing as singleton problem was still there. – Baboon Dec 17 '12 at 00:02
  • @Eugene if you use `using` statement, you are awaiting that GC is called immediately after you leave it. Otherwise there is no sense to use it. Tell me at least one good reason to not call GC manually at such place? – VladL Dec 17 '12 at 08:34
  • I agree with Eugene. Calling GC is nearly always a code smell. You should not have to do it. – AndyD Dec 17 '12 at 09:45
  • @AndyD ok, maybe you will tell me the reason? – VladL Dec 17 '12 at 10:57
  • Quote from MSDN: `GC.Collect()` Method. All objects, regardless of how long they have been in memory, are considered for collection; however, objects that are referenced in managed code are not collected. Use this method to force the system to try to reclaim the maximum amount of available memory. – VladL Dec 17 '12 at 11:04
  • http://stackoverflow.com/questions/478167/when-is-it-acceptable-to-call-gc-collect In this case I just can't see why it would help and the only reason it might be a solution is because it blocks all threads and somehow sorts the multithreading issue out. I would like to see an explanation of how calling GC.Collect() in this specific case fixes the issue. I'm not saying it doesn't, but I want to understand how as I just can't see it. – AndyD Dec 17 '12 at 12:38
  • @AndyD very simple, at the time you are leaving ´using´, opened ´FileStream´ will be "marked" for the GC to clear. The problem is that you never know at what time GC will run in automatic mode. If you would run the code less often, you wouldn't note the difference, but the exception is thrown if OP clicks too fast so running ´GC.Collect()´ is the only solution, even ´FileStream.Close()´ wouldn't help. – VladL Dec 17 '12 at 12:46
  • so filestream.close doesn't release the actual OS file handle? – AndyD Dec 17 '12 at 13:14
  • AndyD exactly. ´using´ calls the same Close() for you, but this just marks the reference to be cleared next time GC is running. BTW it is the same for ´httpwebrequest´ for example. – VladL Dec 17 '12 at 13:18
  • I think, that problem is not in using{} section but in XMLSerializer itself - as mentioned under - it creates some temp files (but in the exception the XML file is used, not some temp). – Baboon Dec 17 '12 at 13:20
  • @Baboon it's because the problem is not what you think, but what I described above – VladL Dec 17 '12 at 13:25