Hey all,
I'm trying to write a program with a couple background workers in it. In the background worker DoWork() function I have a few EventLog calls that call a delegate to another function with a textwriter inside. I'm using the background worker reportprogress for something else so I would really like to know what is wrong with my code below? I get errors that another thread is using the textwriter.

public delegate void EventLogCallback(string newEvent);
public partial class Main : Form
    {
        EventLogCallback EventLog;
        
        public Main()
        {
            InitializeComponent();
            EventLog = new EventLogCallback(AddEventLog);
        }

        private void Main_Load(object sender, EventArgs e)
        {
            bwDatabase.RunWorkerAsync();
            for (int i = 0; i < 1000; i++)
            {
                if (i % 2 == 1)
                    EventLog("Main: " + i.ToString()); //odd
                else
                    EventLog("Main: " + i.ToString()); //even
            }
        }

private void AddEventLog(string eventMsg)
        {
            string filename = @"c:\Log\Log for " + DateTime.Now.Month.ToString() + "-" + DateTime.Now.Day.ToString() + "-" + DateTime.Now.Year.ToString() + ".txt";
            if (eventMsg != sLastEvent)
            {
                sLastEvent = eventMsg;
                eventMsg = DateTime.Now.ToString() + ": " + eventMsg;
                system.EventList.Add(eventMsg);
                TextWriter wText = new StreamWriter(filename,true);
                wText.WriteLine(eventMsg);
                wText.Close();
            }
        }

private void bwDatabase_DoWork(object sender, DoWorkEventArgs e)
        {
            EventLog.Invoke("Database Thread Started");
            for (int i = 0; i < 1000; i++)
            {
                if (i % 2 == 1)
                    EventLog("SQL: " + i.ToString()); //odd
                else
                    EventLog.Invoke("SQL: " + i.ToString()); //even
            }

As you can see, I tried EventLog directly and EventLog.invoke to no avail. What am I doing wrong?

Thanks,
Jeremy

>> "I get errors that another thread is using the textwriter"

You can only have a file open once. If two threads open the textwriter to the same file you'll start seeing those errors. What you need to do is funnel all of the File I/O operations through a single thread, or, make the file writing thread safe. Here is an example of writing a file in a thread safe manner:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace daniweb
{
  public partial class frmThreadSafe : Form
  {
    const string logFileName = @"C:\logger.txt";
    private int threadCount;

    public frmThreadSafe()
    {
      InitializeComponent();
    }

    private void frmThreadSafe_Load(object sender, EventArgs e)
    {
      if (File.Exists(logFileName))
        File.Delete(logFileName); //Reset it for testing purposes
    }

    private void button1_Click(object sender, EventArgs e)
    {
      using (ThreadSafeLogWriter logger = new ThreadSafeLogWriter(logFileName))
      {
        threadCount = 0;

        //Increase our thread pool for max IO
        int workerThreads, completionPortThreads;
        System.Threading.ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
        workerThreads += 50;
        System.Threading.ThreadPool.SetMinThreads(workerThreads, completionPortThreads);

        //Lets pull 100 threads off the thread pool for testing
        for (int i1 = 0; i1 < 100; i1++)
        {
          System.Threading.Interlocked.Increment(ref threadCount);
          new Action<ThreadSafeLogWriter,string>(WriteLogMessage).BeginInvoke(
            logger,
            Guid.NewGuid().ToString(), //sample data
            new AsyncCallback(WriteLogMessageCallback),
            null);
        }

        while (threadCount != 0) //reading an int32 is an atomic operation on 32bit machines
        {
          System.Threading.Thread.Sleep(10); //stop context thrashing. Sleep for a little bit
        }
      }
      MessageBox.Show("Done");
    }

    private static void WriteLogMessage(ThreadSafeLogWriter logger, string s)
    {
      logger.AppendLine(s);
      System.Threading.Thread.Sleep(100); //This is for testing so we are forced to create new threads
    }

    //This is just to decrement our handy thread counter
    private void WriteLogMessageCallback(IAsyncResult ar)
    {
      System.Threading.Interlocked.Decrement(ref threadCount);
    }
  }

  public class ThreadSafeLogWriter : IDisposable
  {
    private string logfile;
    private TextWriter writer;
    
    private ThreadSafeLogWriter()
    {
    }
    public ThreadSafeLogWriter(string FileName)
      : this()
    {
      this.logfile = FileName;
      writer = new StreamWriter(this.logfile, true);
    }
    public void AppendLine(string LogMessage)
    {
      lock (writer)
      {
        writer.WriteLine(string.Format("[{0:F0}]: {1}", System.Threading.Thread.CurrentThread.ManagedThreadId, LogMessage));
      }
    }
    #region IDisposable Members
    public void Dispose()
    {
      if (writer != null)
      {
        writer.Flush();
        writer.Close();
        writer.Dispose();
        writer = null;
      }
    }
    #endregion
  }
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.