I simulated the process and got some unexpected results.
Basically I created a MenuWriter class that has a static
method Write() and
a static FileStream object "fs". In that regard it's a close
implementation
of your menuWriter class.
Here is what the Write() method originally looked like:
// MenuWriter.Write - simulates 2 second operation
static void Write()
{
try
{
fs = new FileStream( @"c:\dev\file.xml", FileMode.Create,
FileAccess.Write, FileShare.None );
lock ( fs )
{
Console.WriteLine( "thread {0}",
Thread.CurrentThread.ManagedThreadId );
Thread.Sleep(2000);
}
}
catch{}
finally{ fs.Close(); }
}
The Main() method created an array of 20 threads. Each thread
would start
the Write method.
void Main()
{
int max = 20;
Thread[] threads = new Thread[max];
for ( int i = 0; i < max; i++ )
{
threads
= new Thread( new ThreadStart(MenuWriter.Write));
}
for ( int i = 0; i < max; i++ )
{
threads.Start();
}
}
What I expected to happen was that every 2 seconds the line
"thread ##"
would be written to the console, where ## is the unique
thread ID.
I expected to see this output 20 times, occuring every 2
seconds.
What happened was either:
a. Only about 7-9 lines would be written before the program
terminated
without throwing an exception. There was no delay in between
each line being
written.
or
b. 7-9 lines written with no delay and an
NullReferenceException would occur
pointing to Line 52 which was the "fs.Close()" line in the
finally block.
Even if all 20 lines were written with no exception, the lack
of the 2
second delay tells me that there was no lock, or the lock is
implemented
incorrectly.
So, 2 things to correct that and get the expected results:
1. Make the FileStream object local to the method.
2. Lock something other than the FileStream object.
The re-write:
class MenuWriter
{
private static object syncLock = new object(); // the object
to lock
public static void Write()
{
lock ( syncLock )
{
// do all the work in here
FileStream fs = new FileStream( .... );
try
{
string message = "Written by " +
Thread.CurrentThread.ManagedId.ToString();
fs.Write(Encoding.ASCII.GetBytes(message), 0,
message.Length);
Thread.Sleep(2000);
}
catch{}
finally{ fs.Close(); }
}
}
}
With those changes, I observed 20 lines output to the
console, occuring
every 20 seconds, which I believe simulates what you want to
accomplish.
Additionally, I went ahead and wrote to a text file in the
Write() method as
shown above. Where the console output that Thread 22 was the
last thread to
get the lock, this was also written to the text file,
verifying that 20
threads waited for the lock, acquired it, wrote to the file,
released the
lock.
Also note that Thead.Sleep(2000) is just to simulate a
process that takes 2
seconds and give me a change to see the threads queue in line
waiting for
the lock.
Ron
"darrel" <notreal@nowhere.com> wrote in message
news:e529b2$eqv$1@forums.macromedia.com...
>
>> What's the problem?
>
> I'll try to explain. ;o)
>
> I have an abridged version of the funtion at the bottom
of this post if
> you want to take a look at it.
>
> The scenario:
>
> Our CMS, whenever a page is updated on the back end,
calls a function that
> then queries the DB recursively to generate an XML file
of all the data.
> The intent was that the XML file is easy to cache on the
front-end, so we
> can use it to store all the page-centric variables and
to generate all the
> site menus without having to make additional hits on the
DB on each page
> load.
>
> The problem is that I THINK we're having threading
issues on the back end.
> As more people use the CMS, we're finding more errors
that we think are
> related to the XML file function either being
'interupted' by another
> call, or being locked in the filesystem itself. I'm not
sure if it's bad
> code on my part, or the Windows File system just not
handling things very
> well.
>
> At this point, I'm just out of energy to fight it
anymore. I think I may
> just store the XML back into the DB and let the DB
handle all the
> threading/locking issues for me.
>
> That said, I'd love to know what, specifically, I'm
doing wrong, so if you
> see anyting amiss, please let me know!
>
> -Darrel
>
>
> My class file:
>
-------------------------------------------------------------
> Imports System.IO
> Public Class menuWriter
>
> Shared fs As System.IO.FileStream
>
> Shared Sub createXMLfileWithXmlWriter(ByVal parentNode
As Integer,
> ByVal intLevel As Integer, ByVal rowCount As Integer,
ByVal
> createParentNode As Boolean, ByVal siteID As Integer)
>
> Try
>
> Dim objXMLWriter As System.Xml.XmlTextWriter
> Dim path As String =
>
System.Web.HttpContext.Current.Server.MapPath("/mjb05/xml/" &
siteID &
> "/siteMenu.xml").ToString
> fs = New FileStream(path, FileMode.Create,
FileAccess.Write,
> FileShare.None)
> SyncLock fs
> objXMLWriter = New System.xml.XmlTextWriter(fs,
> System.Text.Encoding.Default)
> objXMLWriter.Formatting =
objXMLWriter.Formatting.Indented
> objXMLWriter.Indentation = 3
> objXMLWriter.WriteStartDocument()
> objXMLWriter.WriteComment("Created on " & Now())
> objXMLWriter.WriteStartElement("menuItems")
>
> WriteXmlData(objXMLWriter, parentNode, siteID)
>
> objXMLWriter.WriteEndElement() 'close menuItems node
> objXMLWriter.WriteEndDocument() 'close docment
> objXMLWriter.Flush()
> objXMLWriter.Close()
> End SyncLock
> Catch ex As Exception
> ...catch errors
> Finally
> fs.Close()
> End Try
>
> End Sub
>
>
> Shared Sub WriteXmlData(ByVal objXMLWriter As
System.Xml.XmlTextWriter,
> ByVal parentNode As Integer, ByVal siteID As Integer,
Optional ByVal
> intLevel As Int32 = 0)
>
> 'retrieve all children of parentNode
> ' grab the data from the db
> Dim DS As New DataSet
> ...set up DB connection
> objOleDbAdapter.Fill(DS, "webPages")
>
>
> ' Walk through results whether it's a list or a single
contact.
> ' based off of this tutorial:
> '
>
http://www.wwwcoder.com/main/parentid/154/site/899/68/default.aspx
>
> Dim rowCount As Int32 = 0
> While rowCount < DS.Tables(0).Rows.Count 'ie, if
there IS
> data, then...
> objXMLWriter.WriteStartElement("menuItem")
>
>
> objXMLWriter.WriteElementString("pageID",
> Trim(DS.Tables(0).Rows(rowCount)("pageID").ToString))
> objXMLWriter.WriteElementString("parentID",
> Trim(DS.Tables(0).Rows(rowCount)("parentID").ToString))
> ...write rest of elements.
>
> 'now call the subroutine we're in to see if this value
has
> 'any children and increase the indent, and so on...
> WriteXmlData(objXMLWriter,
> Convert.ToInt32(DS.Tables(0).Rows(rowCount).Item(0)),
siteID, intLevel +
> 1)
> rowCount = rowCount + 1
> objXMLWriter.WriteEndElement() 'close menuItem
> node
> End While
>
> ' clean up
>
> objConnect.Close()
> objOleDbAdapter.Dispose()
> objCommand.Dispose()
>
> End Sub
>
> End Class
>
>