在 C# 中的多线程中共享单个数据表
Share Single Datatable in Multi Threading in C#
我有一个应用程序,它有三个线程。访问单个数据表。
每个线程一次打印一行。它不应该 repeat.but 在我的应用程序中,所有三个线程都打印数据表中的所有行。
我希望一行只打印一次。
class Program
{
static void Main(string[] args)
{
ThreadStart testThread1Start = new ThreadStart(new program().testThread1);
ThreadStart testThread2Start = new ThreadStart(new Program().testThread2);
Thread[] testThread = new Thread[2];
testThread[0] = new Thread(testThread1Start);
testThread[1] = new Thread(testThread2Start);
foreach (Thread myThread in testThread)
{
myThread.Start();
}
Console.ReadLine();
}
private void testThread2()
{
DataTable dt = SQLite.ExecuteQuery("SELECT id,name,address FROM example ");
if (dt.Rows.Count != 0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
lock (dt.Rows[i])
{
var Id = dt.Rows[i]["Id"].ToString();
SQLite.ExecuteNonQuery("update example set DisplayDataStatus = 1 where id= '" + Id + "' ");
var name = dt.Rows[i]["name"].ToString();
var address = dt.Rows[i]["address"].ToString();
Console.WriteLine("ID:"+Id+"|Name:"+name+"|address:"+adress);
}
Console.ReadLine();
}
}
else
{}
}
private void testThread1()
{
DataTable dt = SQLite.ExecuteQuery("SELECT id,name,address FROM example ");
if (dt.Rows.Count != 0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
lock (dt.Rows[i])
{
var Id = dt.Rows[i]["Id"].ToString();
SQLite.ExecuteNonQuery("update example set DisplayDataStatus = 1 where id= '" + Id + "' ");
var name = dt.Rows[i]["name"].ToString();
var address = dt.Rows[i]["address"].ToString();
Console.WriteLine("ID:"+Id+"|Name:"+name+"|address:"+adress);
}
Console.ReadLine();
}
}
else
{}
}
}
第一个
为什么要在每个线程中重新执行“SQLite.ExecuteQuery
”?如果只想打印table中的所有行而不阻塞UI,有一个单后台执行查询就够了Thread
然后打印行。
让更多线程尝试从 same 数据库读取并尝试写入 same Console
,反而会减慢您的应用程序加快它的速度(因为执行时间是相同的,但你必须总结线程之间所有上下文切换所需的时间)。
第二个
好的,假设你还需要3个线程来执行其他并行操作。
假设 DataTable
实例对于所有线程都是相同的。
Row
上的锁仅阻止线程同时写入 Row
。假设 Thread
T1 正在写 Row
R1。 T2 在 R1 上被阻塞,无法写入。然后 T1 完成,解锁 R1 并阻止 R2。因为现在R1已经解封,T2进入R1的lock
段写入R1。等等。所以所有线程都写入所有行,但在不同的时刻。
假设每个 Thread
都像您的代码那样创建自己的 DataTable
。如果 T1 锁定 dt.Row[0]
,那与 T2 的 dt.Row[0]
不是同一个对象!它们是独立的对象,因此锁定部分不会阻止任何其他线程的执行!
第三
“if (dt.Rows.Count != 0)
”语句完全没用。
当您进入 for
循环并且没有行时,循环会在第一次检查时退出而不会产生任何后果。
在 for/foreach 循环之前有意义的检查是针对空列表的检查。所以你可以这样写:
"if (Rows != null) { for (int i = 0; i < Rows.Count; i++) { ... ... ... }
"
更新
如果您只需要将行打印到控制台,您可以有 1000 个线程,但由于它们都打印到同一个控制台,所以您有 999 个无用线程。如果您必须对一行执行某些 "expensive" 操作,然后 然后 将其显示到控制台,情况就不同了。在这种情况下,试试这个:
- 执行仅检索行计数的查询。
- 根据总计数将所有行分为三个范围,为每个线程分配一个范围并启动每个线程。例如T1只查询0-14999行,T2查询15000-29999行,以此类推。
- 传递给每个线程的方法可以相同,但输入的
startRow
和 endRow
数字不同。该方法仅使用这些行创建一个 DataTable,并仅将这些行打印到控制台。
无论如何,此解决方案不会按顺序打印所有行。这是要求吗?
我有一个应用程序,它有三个线程。访问单个数据表。 每个线程一次打印一行。它不应该 repeat.but 在我的应用程序中,所有三个线程都打印数据表中的所有行。
我希望一行只打印一次。
class Program
{
static void Main(string[] args)
{
ThreadStart testThread1Start = new ThreadStart(new program().testThread1);
ThreadStart testThread2Start = new ThreadStart(new Program().testThread2);
Thread[] testThread = new Thread[2];
testThread[0] = new Thread(testThread1Start);
testThread[1] = new Thread(testThread2Start);
foreach (Thread myThread in testThread)
{
myThread.Start();
}
Console.ReadLine();
}
private void testThread2()
{
DataTable dt = SQLite.ExecuteQuery("SELECT id,name,address FROM example ");
if (dt.Rows.Count != 0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
lock (dt.Rows[i])
{
var Id = dt.Rows[i]["Id"].ToString();
SQLite.ExecuteNonQuery("update example set DisplayDataStatus = 1 where id= '" + Id + "' ");
var name = dt.Rows[i]["name"].ToString();
var address = dt.Rows[i]["address"].ToString();
Console.WriteLine("ID:"+Id+"|Name:"+name+"|address:"+adress);
}
Console.ReadLine();
}
}
else
{}
}
private void testThread1()
{
DataTable dt = SQLite.ExecuteQuery("SELECT id,name,address FROM example ");
if (dt.Rows.Count != 0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
lock (dt.Rows[i])
{
var Id = dt.Rows[i]["Id"].ToString();
SQLite.ExecuteNonQuery("update example set DisplayDataStatus = 1 where id= '" + Id + "' ");
var name = dt.Rows[i]["name"].ToString();
var address = dt.Rows[i]["address"].ToString();
Console.WriteLine("ID:"+Id+"|Name:"+name+"|address:"+adress);
}
Console.ReadLine();
}
}
else
{}
}
}
第一个
为什么要在每个线程中重新执行“SQLite.ExecuteQuery
”?如果只想打印table中的所有行而不阻塞UI,有一个单后台执行查询就够了Thread
然后打印行。
让更多线程尝试从 same 数据库读取并尝试写入 same Console
,反而会减慢您的应用程序加快它的速度(因为执行时间是相同的,但你必须总结线程之间所有上下文切换所需的时间)。
第二个
好的,假设你还需要3个线程来执行其他并行操作。
假设 DataTable
实例对于所有线程都是相同的。
Row
上的锁仅阻止线程同时写入 Row
。假设 Thread
T1 正在写 Row
R1。 T2 在 R1 上被阻塞,无法写入。然后 T1 完成,解锁 R1 并阻止 R2。因为现在R1已经解封,T2进入R1的lock
段写入R1。等等。所以所有线程都写入所有行,但在不同的时刻。
假设每个 Thread
都像您的代码那样创建自己的 DataTable
。如果 T1 锁定 dt.Row[0]
,那与 T2 的 dt.Row[0]
不是同一个对象!它们是独立的对象,因此锁定部分不会阻止任何其他线程的执行!
第三
“if (dt.Rows.Count != 0)
”语句完全没用。
当您进入 for
循环并且没有行时,循环会在第一次检查时退出而不会产生任何后果。
在 for/foreach 循环之前有意义的检查是针对空列表的检查。所以你可以这样写:
"if (Rows != null) { for (int i = 0; i < Rows.Count; i++) { ... ... ... }
"
更新
如果您只需要将行打印到控制台,您可以有 1000 个线程,但由于它们都打印到同一个控制台,所以您有 999 个无用线程。如果您必须对一行执行某些 "expensive" 操作,然后 然后 将其显示到控制台,情况就不同了。在这种情况下,试试这个:
- 执行仅检索行计数的查询。
- 根据总计数将所有行分为三个范围,为每个线程分配一个范围并启动每个线程。例如T1只查询0-14999行,T2查询15000-29999行,以此类推。
- 传递给每个线程的方法可以相同,但输入的
startRow
和endRow
数字不同。该方法仅使用这些行创建一个 DataTable,并仅将这些行打印到控制台。
无论如何,此解决方案不会按顺序打印所有行。这是要求吗?