Domanda

I have this Windows Console Application:

using System;
using System.Text;

namespace ThreadLockSample
{
    class Program
    {
        static void P1(System.Data.DataTable dt)
        {

            for (int i = 0; i < 100000; i++)
            {
                dt.Rows.Add(i);
            }
        }
        static void P2(System.Data.DataTable dt)
        {

            for (int i = 100000; i < 200000; i++)
            {
                dt.Rows.Add(i);
            }
        }

        static void Main(string[] args)
        {
            System.Data.DataTable dt = new System.Data.DataTable();
            for (int i = 0; i < 200; i++)
                dt.Columns.Add("Id " + i);

            System.Threading.Thread t1 = new System.Threading.Thread(() => P1(dt));
            System.Threading.Thread t2 = new System.Threading.Thread(() => P2(dt));
            t1.Start();
            t2.Start();
            t1.Join();
            t2.Join();
        }
    }
}

Now i am passing a DataTable accessible to both threads but the app freezes - how can i change my code to remove this issue.

È stato utile?

Soluzione

When running this application, you won't get a deadlock. Instead you get an error stating that some data is corrupted within the DataTable because the DataTable itself has some internal checks for concurrency.

I suppose this application is a simpler version of your real app. There are a couple of steps you can take to improve your program:

  • Avoid multithreading with shared data

Multithreading with shared data is hard. Can you simplify your program so that you don't need any shared data? Maybe you can run two operations in parallel, return a result and then merge those results. This would safe you from a lot of potential problems.

If that's not possible you can do the following:

  • Add locks

You create a lock object like this:

private static object _lock = new object();

And now you surround calls to your datatable with:

lock (_lock)
{
    dt.Rows.Add(i);
}
  • Don't use Threads directly. Instead use the Task Parallel Library:

A Task object is an intelligent wrapper around a Thread. Using Tasks is recommended.

Task t1 = Task.Run(() => P1(dt));
Task t2 = Task.Run(() => P2(dt));

Task.WaitAll(t1, t2);

UPDATE

Since the original question now changed from deadlock to freezes, there are a couple of other things you need to consider.

In a console application, you will always have to wait at some point until your two tasks finish. This waiting will freeze your UI. Off course you can use clever things like a Timer to check if the tasks are finished or something but a console application really isn't meant for a situation like this.

If however, your app is actually a WinForms or WPF app you should look at using asynchronous code with the new async/await keywords added in C# 5. They work perfectly with Tasks and they let you run code on another thread while the UI keeps responsive. When the Task finishes, the result is merged back on your UI thread and the user keeps a responsive app.

For more information on async/await, you can start here: Asynchronous Programming with Async and Await (C# and Visual Basic)

Altri suggerimenti

In addition to Wouter de Kort's answer:

If you want to use a lock on the DataTable, use the DataTable.Rows.SyncRoot Object like

lock(dt.Rows.SyncRoot)
    //Your code here

because it is the supposed lock object.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top