Solved

Multithreading in C#

Posted on 2010-11-26
10
422 Views
Last Modified: 2012-05-10
Hi,

Can someone provide me the answers for the following.

1) I have a multithreaded application and many threads write into the same log file. What is the efficient way of writing into the log file without one thread overwriting the values of other thread and without any race condition or deadlock. Also if you can provide me the sample code that would be nice.

2) what is the best way of showing error messages from a thread in winforms application?

3) I have a class with several properties. I want to serialize only few properties and leave few properties without serialization. How can we acheive this C# .Net?

Thanks
Goutham
0
Comment
Question by:GouthamAnand
10 Comments
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
1. There are some logging frameworks available that might help make this easier, like log4net, but I haven't used any of them.  The logging requirements I've run into personally have thus far been fairly simple, so I've just used a static class (or a singleton) and put file operations in a lock block.

2. Not sure on this, but I suppose it would to some extent depend on the desired behavior; i.e. calling a modal dialog from a secondary thread I think would block only the window on that thread, whereas if you marshaled the call to the modal dialog back to the main thread it might block every open window.

3. Apply the [NonSerialized] attribute to properties you don't want serialized.

0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
More on 3: The Serializable attribute by default results in all public and private fields (i.e. variables, not properties) to be serialized by the .Net formatters.  Normally your properties will have backing fields, effectively serializing them, however there may be instances where that is not true, or you want to manually control the serialization process for other reasons, in which case you can implement the ISerializable interface.

Here's a super-simple multi-threaded logging example, and serialization example.
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.IO;

using System.Threading;

using System.Runtime.Serialization;



namespace ConsoleApplication1

{

	class Program

	{

		static void Main(string[] args)

		{

			LogWriter.OpenLogFile("C:\\test.txt");



			Thread t1 = new Thread(ThreadOneProc);

			Thread t2 = new Thread(ThreadTwoProc);



			t1.Start();

			t2.Start();



			for (int i = 0; i < 20; i++)

			{

				LogWriter.WriteLogEntry(String.Format(

					"Message from main thread, ID: {0}.", Thread.CurrentThread.ManagedThreadId));

				Thread.Sleep(1500);

			}



			t1.Join();

			t2.Join();



			Console.WriteLine("All Threads Done");

			Console.ReadKey();

		}



		static void ThreadOneProc()

		{

			for (int i = 0; i < 40; i++)

			{

				LogWriter.WriteLogEntry(String.Format(

					"Message from t1 thread, ID: {0}.", Thread.CurrentThread.ManagedThreadId));

				Thread.Sleep(750);

			}

		}



		static void ThreadTwoProc()

		{

			for (int i = 0; i < 80; i++)

			{

				LogWriter.WriteLogEntry(String.Format(

					"Message from t2 thread, ID {0}.", Thread.CurrentThread.ManagedThreadId));

				Thread.Sleep(375);

			}

		}

	}



	[Serializable]

	class SerializableObject

	{

		public int iAmASerializedField;

		private int iAmAlsoASerializedField;

		[NonSerialized]

		public int iAmNotSerialized;

	}



	[Serializable]

	class OtherSerializableObject : ISerializable

	{

		public int iAmNotSerialized;

		private int iAmAlsoNotSerialized;



		public OtherSerializableObject(int i)

		{

			iAmNotSerialized = i;

		}



		protected OtherSerializableObject(SerializationInfo info, StreamingContext context)

		{

			iAmNotSerialized = info.GetInt32("sortaSerializedProp");

		}



		public void GetObjectData(SerializationInfo info, StreamingContext context)

		{

			info.AddValue("sortaSerializedProp", this.SerializedSortaProperty);

		}



		public int SerializedSortaProperty

		{

			get { return iAmNotSerialized * 2; }

		}

	}



	static class LogWriter

	{

		static object threadLock = new object();

		static FileStream logStream;



		// Note that OpenLogFile() and CloseLogFile() are NOT thread-safe

		public static void OpenLogFile(string LogFile)

		{

			if (logStream != null)

				throw new Exception("Log file is already open.");

			else

				logStream = new FileStream(LogFile, FileMode.Append, FileAccess.Write, FileShare.Read);

		}



		public static void CloseLogFile()

		{

			if (logStream == null)

				throw new Exception("Log file is already closed.");

			else

			{

				logStream.Dispose();

				logStream = null;

			}

		}



		public static void WriteLogEntry(string LogMessage)

		{

			// Once one thread has acquired a lock on "threadLock"

			// other threads attempting to acquire the same lock

			// will be blocked until the first thread exits

			// the "locked" section of code

			lock (threadLock)

			{

				if (logStream == null || !logStream.CanWrite)

					throw new Exception("Unable to write to log file.");



				string message = String.Format(

					"{0:O}: {1}{2}",

					DateTime.Now,

					LogMessage,

					Environment.NewLine);

				byte[] bytes = Encoding.ASCII.GetBytes(message);

				logStream.Write(bytes, 0, bytes.Length);

				logStream.Flush();

			}

		}

	}

}

Open in new window

0
 
LVL 6

Expert Comment

by:ajb2222
Comment Utility
For 1 and 2 I would create functions on your main form to write to the log.  Then from the thread you use the begininvoke method.

MainForm.Invoke((MethodInvoker)delegate() { MainForm.writelog("this is the error"); });

3) add [NonSerialized] in front of the declaration to not serialize
0
 
LVL 20

Expert Comment

by:BuggyCoder
Comment Utility
here are your answers:-

1. use ReaderWriterLockSlim
http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx

2. In Winform have one Queue and a timer. now expose a static method that allows multiple threads to send message to the form. in that method just enque your log text in queue. now allow that timer that at each elapse, deque an item from queue and append it to some text field.

3. Add [NonSerialized] to the fields you don't want to serialize

hope it helps
:-)
0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
1. I think ReaderWriterLockSlim is primarily intended for applications where something is frequently read from, and seldom written to (which is the exact opposite of what you'd normally expect in a logging situation), and performs about 1.5 times slower than Monitor.Enter()/Exit() (at least according to this guy: http://blogs.msdn.com/b/pedram/archive/2007/10/07/a-performance-comparison-of-readerwriterlockslim-with-readerwriterlock.aspx).

3. I think the NonSerializedAttribute was already mentioned (twice). ;)
0
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 
LVL 20

Expert Comment

by:BuggyCoder
Comment Utility
In that case on can use monitor.enter and exit or lock for that matter....
0
 
LVL 11

Expert Comment

by:SAMIR BHOGAYTA
Comment Utility
0
 
LVL 33

Accepted Solution

by:
Todd Gerbert earned 500 total points
Comment Utility
I know, that's why the first two posts (http:#34219191, http:#34219451) suggest & demonstrate using the lock keyword. ;)
0
 

Author Comment

by:GouthamAnand
Comment Utility
Hi,

There is a background process (assume a thread is running) and it processes 50,000 records and for every record , the thread returns an update which needs to be shown in an UI control (assume its a label ) in winform.

what is the efficient way of doing this?

Thanks
0
 
LVL 33

Expert Comment

by:Todd Gerbert
Comment Utility
You can use your form's "Invoke" method to execute code on the same thread that created the form necessary because you can't update a form or it's controls from a thread other than the one that created it (really I should say shouldn't, you can do it but it's a bad idea).

You can also use the BackgroundWorker control to run processing of your records and periodically update progress - using the BackgroundWorker would make life a little easier since it automatically runs UI updates on the correct thread, but you still may need to use Invoke() to do custom updating.

This example is a little idiotic, but it does show how to use a BackgroundWorker to run a thread in the background, reporting progress to the main window - it also shows how to use Form.Invoke() to run code on the main windows' thread.
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.Threading;



namespace WindowsFormsApplication1

{

	public partial class Form1 : Form

	{

		private delegate void ProcessErrorDelegate(int item, string error);

		BackgroundWorker bgw;



		public Form1()

		{

			InitializeComponent();



			bgw = new BackgroundWorker();

			bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);

			bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);

			bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);

			bgw.WorkerReportsProgress = true;

		}



		void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

		{

			// this will run when the background thread completes

			progressBar1.Value = 0;

			progressBar1.Enabled = false;

			button1.Enabled = true;

		}



		void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)

		{

			// This code will run when the background thread updates it's progress

			progressBar1.Value = e.ProgressPercentage;

		}



		void bgw_DoWork(object sender, DoWorkEventArgs e)

		{

			// This is the code that will run on the background thread

			for (int i = 0; i < 100; i++)

			{

				// Report normal progress change

				bgw.ReportProgress(i);



				// Simulate reporting an error

				if (i == 20)

					Invoke(new ProcessErrorDelegate(ProcessError), i, "Test Error Message");





				// Another way to use invoke to run code on main thread

				if (i == 80)

				{

					Invoke(new MethodInvoker(delegate()

					{

						errorLabel.ForeColor = Color.Blue;

						errorLabel.Text = "At item 80";

					}));

				}





				Thread.Sleep(325);

			}

		}



		private void button1_Click(object sender, EventArgs e)

		{

			button1.Enabled = false;

			progressBar1.Enabled = true;

			

			bgw.RunWorkerAsync();

		}



		private void ProcessError(int item, string error)

		{

			errorLabel.Text = String.Format(

				"Error on item {0}: {1}", item, error);

		}



		

	}

}

Open in new window

0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

This article introduced a TextBox that supports transparent background.   Introduction TextBox is the most widely used control component in GUI design. Most GUI controls do not support transparent background and more or less do not have the…
Entity Framework is a powerful tool to help you interact with the DataBase but still doesn't help much when we have a Stored Procedure that returns more than one resultset. The solution takes some of out-of-the-box thinking; read on!
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now