C# C多线程一些线程似乎没有运行
我有一个项目处理应用程序,我想并行处理多个项目。但是,有时它似乎根本不处理一个项目,有时它处理不止一次。下面是我的代码 在计时器计时时,将创建一个新线程,该线程从数据库中准备一个项目列表,然后锁定每个项目,以便在下一个循环中不会拾取它C# C多线程一些线程似乎没有运行,c#,multithreading,C#,Multithreading,我有一个项目处理应用程序,我想并行处理多个项目。但是,有时它似乎根本不处理一个项目,有时它处理不止一次。下面是我的代码 在计时器计时时,将创建一个新线程,该线程从数据库中准备一个项目列表,然后锁定每个项目,以便在下一个循环中不会拾取它 SqlConnection connection = GetConnection(); string selectCommand = my_select_command_where_IsLocked_is_null; List<Item> itemLi
SqlConnection connection = GetConnection();
string selectCommand = my_select_command_where_IsLocked_is_null;
List<Item> itemList = new List<Item>();
using(SqlDataAdapter adapter = new SqlDataAdapter(selectCommand, connection))
{
using(SqlCommandBuilder cbCommandBuilder = new SqlCommandBuilder(adapter))
{
DataTable dtItemStore = new DataTable();
adapter.Fill(dtItemStore);
int totalRows = dtItemStore.Rows.Count;
if(totalRows > 0)
{
adapter.UpdateCommand = cbCommandBuilder.GetUpdateCommand();
foreach(DataRow row in dtItemStore.Rows)
{
Item pickedItem = new Item();
pickedItem.Key = row["Key"].ToString();
// Set all Item properties
itemList.Add(pickedItem);
row["IsLocked"] = 1;
}
}
adapter.Update(dtItemStore);
}
}
项目处理器功能在循环结束时解锁项目
function ProcessItem(Item pickedItem)
{
Logger.Log("Let's process !!! " + pickedItem.Key);
// Item processing code
UnlockItem(pickedItem); // unlock and write log
}
问题是,过了一段时间,我意识到我的数据库中有太多的锁定项,而且数量还在增加。在特定点登录,我得到如下结果
- Spawn thread: 20779205, ThreadState: Running
- Let's process !!! 20779205
- Spawn thread: 20779206, ThreadState: Running
- Let's process !!! 20779206
- Spawn thread: 20779207, ThreadState: Running
- Let's process !!! 20779207
- Spawn thread: 20779208, ThreadState: Running <---Not processed
- Let's process !!! 20779209 <---Duplicate
- Spawn thread: 20779209, ThreadState: Running
- Let's process !!! 20779209
- Spawn thread: 20779211, ThreadState: Running <---Not processed
- Let's process !!! 20779213
- Spawn thread: 20779213, ThreadState: Running
- Let's process !!! 20779228
- Spawn thread: 20779228, ThreadState: Running
- Let's process !!! 20779231
- Let's process !!! 20779231 <---Duplicate
- Spawn thread: 20779231, ThreadState: Running
- Spawn thread: 20779237, ThreadState: Running
- Keys picked: 20779205, 20779206, 20779207, 20779208, 20779209, 20779211, 20779213, 20779228, 20779231, 20779237,
- Let's process !!! 20779237
- STOP processing. Possible duplicate for 20779209
- STOP processing. Possible duplicate for 20779231
- Unlock Key: 20779209, Row count:1
- Unlock Key: 20779231, Row count:1
- Unlock Key: 20779209, Row count:1
- Unlock Key: 20779237, Row count:1
- Unlock Key: 20779228, Row count:1
- Unlock Key: 20779206, Row count:1
- Unlock Key: 20779207, Row count:1
- Unlock Key: 20779205, Row count:1
- Unlock Key: 20779231, Row count:1
在这里,我可以看到有时候ProcessItem没有被点击20779208、20779211。我不明白为什么这会是。。。花了两天的时间,我完全迷路了
这是多线程环境中的典型情况吗?我怎样才能更好地处理这件事?我必须确保每个项目都处理一次,而且只处理一次
此外,我意识到有时同一个项目会被多次处理20779209、20779231,但是,为了避免重复,我在ProcessItem函数中处理了它。这两种情况之间有联系吗
我做错了什么?日志显示,在执行委托{ProcessItempickedItem;}之前,foreach循环将pickedItem分配给下一个值
您应该在foreach循环和ProcessItem函数之间使用同步系统或添加同步系统。从拾取的输出行键:20779205、20779206、20779209、20779211、20779213、20779228、20779231、20779237,我可以断定所有项目都被击中一次
可能您应该将代码修改为:
var keyList = "";
foreach(Item pickedItem in itemList)
{
Thread.Sleep(15); // Just to delay a bit
Item tmpItem = pickedItem; -- the same variable should not be accessed by many threads
var newThread = new Thread(delegate() { ProcessItem(tmpItem); });
newThread.Start();
Logger.Log(" Spawn thread: " + pickedItem.Key + ", ThreadState: " + newThread.ThreadState);
keyList = keyList + pickedItem.Key;
}
Logger.Log("Keys picked: " + keyList);
您真的需要“手动”生成线程吗? 将此任务留给框架执行:
itemList.AsParallel().ForAll((item) =>
{
Logger.Log("Processing item ...");
ProcessItem(item);
});
现在您需要做的就是确保ProcessItem方法是线程安全的
编辑
ForAll将阻止当前线程,直到集合中每个项目的操作完成。是否可能在检索项目后但在将其更新为锁定之前,计时器再次启动?您是否确认该字段已设置为锁定?SQL Profiler在DB上显示的查询顺序是什么?@MattC,是的,计时器可能正在启动,但在这种情况下,我使用IsProcessRunning字段跳过该进程。。。我将更新我的代码。是的,行被锁定了。我会检查分析器…你在循环中标记你更改的迭代器行?!?!?!作为IsLocked,但是检查行是否被锁定的代码在哪里?@PaulZahra,检查在我的查询中我的_select_命令中,其中_IsLocked_为空,类似于从IsLocked为空的项目中选择*。谢谢你的建议。以前没有做太多的线程。。。几乎是一个学习者。我当然会尝试一下。看起来在C5中foreach已经更改,因此不再需要声明局部变量:
itemList.AsParallel().ForAll((item) =>
{
Logger.Log("Processing item ...");
ProcessItem(item);
});