views:

1384

answers:

3

What is the best way to ensure thread-safe Linq-to-XML, for writing?

We recently got overloaded on our web cluster and I had to pull a quick overloaded.aspx out of my butt to capture emails that people can be contacted with later when the site becomes more responsive. In my short 5 minute haste, I wrote this:

private static object LockHandle = new object();

protected override void OnLoad(EventArgs e)
{
  SubmitButton.ServerClick += new EventHandler(SubmitButton_ServerClick);
  base.OnLoad(e);
}

void SubmitButton_ServerClick(object sender, EventArgs e)
{
  string email = Email.Value.Trim();
  if (email.Length > 0)
  {
    Regex regex = new Regex(@"^([\w\-\.]+)@((\[([0-9]{1,3}\.){3}[0-9]{1,3}\])|(([\w\-]+\.)+)([a-zA-Z]{2,4}))$");
    if (regex.IsMatch(email))
    {
      lock (LockHandle)
      {
        try
        {
          string fileName = Server.MapPath("emails.xml");
          XDocument xdoc = XDocument.Load(fileName);
          xdoc.Element("emails").Add(new XElement("email", email));
          xdoc.Save(fileName);
        }
        catch {}
      }

      ResponseText.Text = "Thanks! We'll get back to you.";


    }
  }
}

I wasn't able to lookup if Linq-to-XML was threadsafe; so, the theory went "let me just lock a static object, and that will prevent multiple writes." Was this the best approach? Was it even needed (is Linq-to-Xml thread safe?). Sad to say my two Pro LINQ and Linq in Action books don't touch on that subject.

This worked fine, and we captured an amount of emails in the 20m we were overloaded. Just wondering if there was a better way; or, if it was just overkill to lock it in the first place.

+1  A: 

In general, on MSDN, if a type or non-static member is not explicitly marked as thread safe it isn't. Generally static members should be.

Assuming MS' own guidelines are being followed that is.

However you seem to be wanting to protect a file from being changed while performing a load/edit/save group. And that is only thread safe if you maintain a lock (which is easy enough if you directly create a FileStram with appropriate read/write and then pass that (with a StreamReader or StreamWriter wrapper) to the Load and Save methods).

The disadvantage of just using a lock is the file open methods (including those implicitly called on object construction) will throw an exception if the file is locked. So you might need to have a lock to avoid this (and that might need to be a mutex if you have more than one process or app-domain involved).

Richard
The idea was that I did not want the other writes to fail, but to wait their turn to write to the same file. My thought was that by create a static object and locking it, this would block all other threads from writing (in the same namespace, yes).Yes no?
eduncan911
s/namespace/app-domain/ and yes.But this is about files and concurrency, not XDocument. You would get the same problem loading/manipulating/saving with a List<byte>.
Richard
+1  A: 

I would guess LINQ objects follow the same thread-safety guidelines that any .Net SDK object follows:

  • Calls to static methods do not need to be synchronized across threads
  • Calls to the same instance object should be synchronized across threads
Frank Schwieterman
Yeah, that's the thing. It's always a "guess" unless there is an issue. Hence why I went down this road. Hehe.
eduncan911
Maybe I should say "expect" rather than "guess". This is something you can take for granted when you use components in the System namespace.
Frank Schwieterman
+1  A: 

It is not inherently thread safe to save a file this way, therefore it was smart to use a static lock handle.

BC