Data View Thread Safety Problem

S

Shaeney

I have a multi-threaded application with a static constructor and static
DataSet and DataView variables, populated in the constructor.
i then create many threads to access the static DataView but some of the
threads generate errors while others work. Here is the code for the sample
console app

using System;
using System.Data;
using System.Threading;

namespace DataViewTest
{
class DataViewTest
{
static DataSet _ds;
static DataView _dv;

private volatile Thread _thread;
private string _name;

static DataViewTest()
{
_ds = new DataSet();
DataTable tableOne = new DataTable("TableOne");

tableOne.Columns.Add(new DataColumn("Force", typeof (int)));
tableOne.Columns.Add(new DataColumn("Section", typeof (string)));
tableOne.Columns.Add(new DataColumn("ApplicationID", typeof (string)));
tableOne.Columns.Add(new DataColumn("GroupID", typeof (int)));
tableOne.Columns.Add(new DataColumn("Name", typeof (string)));

DataRow newRow = tableOne.NewRow();
newRow["Force"] = 13;
newRow["Section"] = "0";
newRow["ApplicationID"] = "FA";
newRow["GroupID"] = 1;
newRow["Name"] = "ZZ";
tableOne.Rows.Add(newRow);

_dv = new DataView(tableOne, null, "GroupID", DataViewRowState.CurrentRows);

_ds.Tables.Add(tableOne);
_ds.AcceptChanges();
}

public DataViewTest(string threadName)
{
_name = threadName;
}

internal void Run()
{
if(_dv[0]==null)
{
System.Diagnostics.Trace.WriteLine(string.Format("{0} FAILED", _name));
}
else
System.Diagnostics.Trace.WriteLine(string.Format("{0} worked", _name));
}

internal void Start()
{
_thread = new Thread(new ThreadStart(Run));
_thread.Name = _name;
_thread.IsBackground = true;
_thread.Start();
}
}

/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
private static DataViewTest[] _workers;

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
_workers = new DataViewTest[100];
for (int ct=0; ct<_workers.Length; ct++)
{
_workers[ct] = new DataViewTest(string.Format("Thread '{0}'", ct));
}

for (int ct=0; ct<_workers.Length; ct++)
{
_workers[ct].Start();
}

while(true)
Thread.Sleep(1000);

}
}
}


here is some sample output
Thread '0' worked
The thread 'Thread '0'' (0x1dd4) has exited with code 0 (0x0).
Thread '27' worked
The thread 'Thread '27'' (0x1f18) has exited with code 0 (0x0).
Thread '63' worked
Thread '6' worked
Thread '1' FAILED
The thread 'Thread '6'' (0x1e70) has exited with code 0 (0x0).
The thread 'Thread '1'' (0x1b08) has exited with code 0 (0x0).
Thread '3' FAILED
The thread 'Thread '3'' (0x14d8) has exited with code 0 (0x0).
Thread '11' worked
Thread '2' FAILED
The thread 'Thread '11'' (0x1d90) has exited with code 0 (0x0).
The thread 'Thread '2'' (0x1bd8) has exited with code 0 (0x0).
Thread '5' worked

The documentation says that DataViews are threadsafe, so why is this
happening? Sometimes, everything is OK, but more often than not, at least one
thread fails.
 
C

Cor Ligthert [MVP]

Shaeney,

I wont believe that the dataview (especially the 2005 version) will ever
work in another thread than the datatable. The dataview is dynamic, so every
change in the datatable (and visa versa) will afect it.

Cor

Shaeney said:
I have a multi-threaded application with a static constructor and static
DataSet and DataView variables, populated in the constructor.
i then create many threads to access the static DataView but some of the
threads generate errors while others work. Here is the code for the sample
console app

using System;
using System.Data;
using System.Threading;

namespace DataViewTest
{
class DataViewTest
{
static DataSet _ds;
static DataView _dv;

private volatile Thread _thread;
private string _name;

static DataViewTest()
{
_ds = new DataSet();
DataTable tableOne = new DataTable("TableOne");

tableOne.Columns.Add(new DataColumn("Force", typeof (int)));
tableOne.Columns.Add(new DataColumn("Section", typeof (string)));
tableOne.Columns.Add(new DataColumn("ApplicationID", typeof (string)));
tableOne.Columns.Add(new DataColumn("GroupID", typeof (int)));
tableOne.Columns.Add(new DataColumn("Name", typeof (string)));

DataRow newRow = tableOne.NewRow();
newRow["Force"] = 13;
newRow["Section"] = "0";
newRow["ApplicationID"] = "FA";
newRow["GroupID"] = 1;
newRow["Name"] = "ZZ";
tableOne.Rows.Add(newRow);

_dv = new DataView(tableOne, null, "GroupID",
DataViewRowState.CurrentRows);

_ds.Tables.Add(tableOne);
_ds.AcceptChanges();
}

public DataViewTest(string threadName)
{
_name = threadName;
}

internal void Run()
{
if(_dv[0]==null)
{
System.Diagnostics.Trace.WriteLine(string.Format("{0} FAILED", _name));
}
else
System.Diagnostics.Trace.WriteLine(string.Format("{0} worked", _name));
}

internal void Start()
{
_thread = new Thread(new ThreadStart(Run));
_thread.Name = _name;
_thread.IsBackground = true;
_thread.Start();
}
}

/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
private static DataViewTest[] _workers;

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
_workers = new DataViewTest[100];
for (int ct=0; ct<_workers.Length; ct++)
{
_workers[ct] = new DataViewTest(string.Format("Thread '{0}'", ct));
}

for (int ct=0; ct<_workers.Length; ct++)
{
_workers[ct].Start();
}

while(true)
Thread.Sleep(1000);

}
}
}


here is some sample output
Thread '0' worked
The thread 'Thread '0'' (0x1dd4) has exited with code 0 (0x0).
Thread '27' worked
The thread 'Thread '27'' (0x1f18) has exited with code 0 (0x0).
Thread '63' worked
Thread '6' worked
Thread '1' FAILED
The thread 'Thread '6'' (0x1e70) has exited with code 0 (0x0).
The thread 'Thread '1'' (0x1b08) has exited with code 0 (0x0).
Thread '3' FAILED
The thread 'Thread '3'' (0x14d8) has exited with code 0 (0x0).
Thread '11' worked
Thread '2' FAILED
The thread 'Thread '11'' (0x1d90) has exited with code 0 (0x0).
The thread 'Thread '2'' (0x1bd8) has exited with code 0 (0x0).
Thread '5' worked

The documentation says that DataViews are threadsafe, so why is this
happening? Sometimes, everything is OK, but more often than not, at least
one
thread fails.
 
S

Shaeney

But the DataTable is static in the example, once it is created and added to
the DataSet, it is never updated again.

Since the DataSet containing the DataTable is itself static, I can see no
reason why some threads fail and some do not. It's worth mentioning that the
threads that fail tend to be the first few threads and the threads that work
tend to be the later theads.
 
C

Cor Ligthert [MVP]

Shaeney,

What do you mean with static in this case, the real meaning of the word
static or the use of the word static in C and C derived languages?

Cor
 
S

Shaeney via DotNetMonster.com

In this particular case, I think I mean both.

The DataSet and DataView variables are declared as static. Since the DataSet
"belongs" to the Dataset, as a variable it is also static.

And the other static is that the data contained within the table is static in
as much as it does not change after initialisation.

So my question would be since the variables are static and the data contained
within the DataTable, and therefore the DataView, is static then how come
some threads ( created and run first ) fail to see the data in the DataView
while other threads ( created and run later ) can see the data?

Hope this explains the problem clearly. It's been driving me nuts!
 
M

Miha Markic [MVP C#]

Hi Shaeney,

Hey man, it seems that you've found a documentation error.
DataView read operation is *not* thread safe and it never was due to lazy
caching.
I'll escalate the issue.

--
Miha Markic [MVP C#, INETA Country Leader for Slovenia]
RightHand .NET consulting & development www.rthand.com
Blog: http://cs.rthand.com/blogs/blog_with_righthand/

Shaeney said:
I have a multi-threaded application with a static constructor and static
DataSet and DataView variables, populated in the constructor.
i then create many threads to access the static DataView but some of the
threads generate errors while others work. Here is the code for the sample
console app

using System;
using System.Data;
using System.Threading;

namespace DataViewTest
{
class DataViewTest
{
static DataSet _ds;
static DataView _dv;

private volatile Thread _thread;
private string _name;

static DataViewTest()
{
_ds = new DataSet();
DataTable tableOne = new DataTable("TableOne");

tableOne.Columns.Add(new DataColumn("Force", typeof (int)));
tableOne.Columns.Add(new DataColumn("Section", typeof (string)));
tableOne.Columns.Add(new DataColumn("ApplicationID", typeof (string)));
tableOne.Columns.Add(new DataColumn("GroupID", typeof (int)));
tableOne.Columns.Add(new DataColumn("Name", typeof (string)));

DataRow newRow = tableOne.NewRow();
newRow["Force"] = 13;
newRow["Section"] = "0";
newRow["ApplicationID"] = "FA";
newRow["GroupID"] = 1;
newRow["Name"] = "ZZ";
tableOne.Rows.Add(newRow);

_dv = new DataView(tableOne, null, "GroupID",
DataViewRowState.CurrentRows);

_ds.Tables.Add(tableOne);
_ds.AcceptChanges();
}

public DataViewTest(string threadName)
{
_name = threadName;
}

internal void Run()
{
if(_dv[0]==null)
{
System.Diagnostics.Trace.WriteLine(string.Format("{0} FAILED", _name));
}
else
System.Diagnostics.Trace.WriteLine(string.Format("{0} worked", _name));
}

internal void Start()
{
_thread = new Thread(new ThreadStart(Run));
_thread.Name = _name;
_thread.IsBackground = true;
_thread.Start();
}
}

/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
private static DataViewTest[] _workers;

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
_workers = new DataViewTest[100];
for (int ct=0; ct<_workers.Length; ct++)
{
_workers[ct] = new DataViewTest(string.Format("Thread '{0}'", ct));
}

for (int ct=0; ct<_workers.Length; ct++)
{
_workers[ct].Start();
}

while(true)
Thread.Sleep(1000);

}
}
}


here is some sample output
Thread '0' worked
The thread 'Thread '0'' (0x1dd4) has exited with code 0 (0x0).
Thread '27' worked
The thread 'Thread '27'' (0x1f18) has exited with code 0 (0x0).
Thread '63' worked
Thread '6' worked
Thread '1' FAILED
The thread 'Thread '6'' (0x1e70) has exited with code 0 (0x0).
The thread 'Thread '1'' (0x1b08) has exited with code 0 (0x0).
Thread '3' FAILED
The thread 'Thread '3'' (0x14d8) has exited with code 0 (0x0).
Thread '11' worked
Thread '2' FAILED
The thread 'Thread '11'' (0x1d90) has exited with code 0 (0x0).
The thread 'Thread '2'' (0x1bd8) has exited with code 0 (0x0).
Thread '5' worked

The documentation says that DataViews are threadsafe, so why is this
happening? Sometimes, everything is OK, but more often than not, at least
one
thread fails.
 
S

Shaeney via DotNetMonster.com

Yeah, I thought as much. For now my work around will be to execute the Find
method on the DataView after I create it in the static constructor. This
should force the lazy initialisation.

Not exactly the solution I was hoping for...
 
M

Miha Markic [MVP C#]

Hi Shaeney,

Shaeney via DotNetMonster.com said:
Yeah, I thought as much. For now my work around will be to execute the
Find
method on the DataView after I create it in the static constructor. This
should force the lazy initialisation.

Not exactly the solution I was hoping for...

I would opt for a proper thread sync instead - use the lock statement any
you'll be on the safe side (of course, this goes for acessing/modifyin
underlying datatable/dataset too). The reason is that there might be other
thread-safety problems in there.
 
C

Cor Ligthert [MVP]

Miha,

Won't probably not work, every row change can affects the dataview by
instance the "sort" property in that, which is set by just clicking on a
datagrid in an UI.

Cor
 
S

Shaeney via DotNetMonster.com

I am lucky that this is a service application we are talking about, so for my
particular case, with everything static, calling the find method in the
static constructor will work for me. I dont have any worries about data grids.


Of course, this does not help anyone else. i would suggest that they have a
thread instance DataView rather than a static one, although I believe there
is also an issue with many DataViews on a single DataSet ( with a hotfix ).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top