Click here to Skip to main content
15,860,861 members
Please Sign up or sign in to vote.
4.75/5 (4 votes)
See more:
Scenario:
Hierarchical Objects (AccessPoint hiearchy during Provisioning) get created in the database. For eg the hierarchy is like this:

Request-1 for one user:
Country1 Committed in Database
Zipcode1 Committed in Database
Home1 Committed in Database
Device1 Committed in Database
Device2 Committed in Database
Device3 Committed in Database

This is happening in a multithreaded environment where something like Request-1 above is for several of users at the same time. Code for multithreading is:

Thread th = new Thread(new ParameterizedThreadStart(ExecuteCreateAccessPtThread));
th.SetApartmentState(ApartmentState.STA);
th.Start(reqDoc);
th.Join();


Method ExecuteCreateAccessPtThread is responsible to create the above hierarchy in the database for all users. There is a check in the code that if Country1 exists do not create it, if Zipcode1 exists do not create it, respectively for Home1 and Device1.

Problem Description:
Behavior of the above mentioned multi threading code is not consistent. Sometimes when there are multiple requests for the same zipcode and looks like database commit has not been performed then at times it is observed that two Zipcode1 rows are created. These cannot be made a primary key since we are using a proprietory database called PI-AF. So now when Home1 tries to find Zipcode1 it gets two rows and hence the entire hierarchy below that is then not created at all.

It would be great if you can let us know, whether the multi thread code above is correct for this scenario.
Posted

You have to give us a little more info about your architecture. Is this in a desktop app? What database are you using? Is the code being inserted via a stored procedure? What does the schema look like? If your database is architected correctly, you shouldn't need to do anything *in your code* to prevent duplicate records from being input.
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 25-Mar-11 12:48pm    
Nevertheless, I already see a fatal mistake. Please see my Answer.
"You don't have to eat whole egg to know if it is bad".
--SA
Аslam Iqbal 25-Mar-11 17:42pm    
good answer. my 5
This is absurd:

C#
th.Start(reqDoc);
th.Join();


This is a problem. You start thread, but join means you suspend calling thread for all time needed the other thread to finish. You calling thread goes to the fatal state and will never be scheduled back until awaken by the finish of the spawn thread.

That means you do not use any parallelism but use one thread at a time anyway. This is not multithreading at all.

Besides, parametrized thread start is bad, you really don't need it; see my code for thread wrapper and this discussion: How to pass ref parameter to the thread[^].

See Answers to similar Questions collected here: How to get a keydown event to operate on a different thread in vb.net[^]. It will give you an ideas on what's involved.

—SA
 
Share this answer
 
If you have multiple threads potentially inserting identical data into a database, and you need to ensure that only the first succeeds (because its successors don't need to modify the database) you need to ensure that the "check that the data doesn't exist and the write to the database" operation is atomic - meaning that while it is happening no other thread can interrupt it and start doing the same work.

This is where you must use the lock(lockObj) { /* ... */ } construct, and you must ensure that all your threads use the constrained code.

Here's some very loose pseudo-code that demonstrates the idea:

namespace ThreadLockPseudoCode
	{
	class SomeData { }
	interface IDatabase
		{
		bool Contains(SomeData data);
		void Write(SomeData data);
		}
	class Program
		{
		private readonly object LockObject = new object();
		private IDatabase TheDatabase;		// Initialized somehow

		// All threads should use this method.
		// Note that your implementation of TheDatabase must be threadsafe
		private void WriteToDatabase(SomeData dataToWrite)
			{
			lock(LockObject)
				{
				// The important part of this pseudo-code is that the two
				// calls on TheDatabase are inside the lock() statement.
 				// Without it, one thread could be suspended
				// after doing the test, allowing another to repeat the
				// test (getting the same result) and do the write;  
				// finally the first thread proceeds with the write, thinking
				// that it is still needed.
				if (TheDatabase.Contains(dataToWrite))
					return;
				TheDatabase.Write(dataToWrite);
				}
			}
		}
	}
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900