问个多线程操作DataTable时遇到的百思不得其解的问题

於黾 2014-11-06 09:05:38
首先说一下软件功能
读取OPC,开一个TCP侦听,允许客户端通过我的软件获取OPC的数据
OPCServer是ABPLC的RSLinx,客户端是Unity3D,用OPC的dll会比较麻烦,所以自己做了个中间件

要读取的数据配置到excel表里
软件开启的时候读取excel表,然后放到datatable里
为了方便通信时关联OPC标签和值,我把值也存放在datatable的新增列里去了
同时,又新增了一列"OPC旧值",这样OPC数据无变化时,可以减少向客户端发送的数据,提高效率
这个datatable是个全局静态变量,定义在program.cs类里,只有在软件开启的时候会add,其他时候仅仅是取出或赋值

一共有3个地方会访问datatable:
1.读取到OPC标签的值之后,更新OPC值
2.客户端通过TCP取数据的时候,取值并拼接到byte数组里,然后用"OPC值"给"OPC旧值"赋值
3.有个窗口可以实时显示这个datatable的结构和值
窗口显示部分,为了避免线程操作UI,我是在打开窗体时先datatable.copy()到一个新的datatable中,再绑定,然后timer里循环判断两个datatable是否有变化,有不同就用全局datatable给局部datatable赋值

这样应该不存在线程不安全的问题才对
但是实际情况是,运行一开始一切正常,运行了一段时间后,datatable里的数据就都窜行了
窜行分两种:
1.整个datatable的顺序都变了,开始我以为是因为观察窗口的datagridview没有禁止排序导致的,但是我在关闭窗口时指定
datagridview.datasource=null,一开始确实可以在排序->关闭->再开之后,恢复默认的排序
但是运行了一段时间后再看,还是会顺序错乱
2.OPC值和OPC旧值对应不上了,整个窜了一行.行号我都是用索引控制的,按理说不应该存在窜行的情况才对

而且在OPC获取到值的回调事件中,有时会报错:索引超出数组界限
传递给OPCdll的是一个ArrayList,这个ArrayList也是在program.cs里定义的全局静态变量,而且是在读文件之后,跟datatable一起初始化的,之后没有代码对它进行任何操作,按理说ArrayList的长度应该跟datatable的行数永远一致才对,而且行数应该永远不变
真伤脑筋
...全文
265 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
master_y 2014-11-30
  • 打赏
  • 举报
回复
说得好
於黾 2014-11-06
  • 打赏
  • 举报
回复
引用 9 楼 layershow 的回复:
[quote=引用 8 楼 Z65443344 的回复:] 现在是两个线程操作的其实不是同一列,是否也必须加锁?
要加锁,刚才我举那个例子就是,如果操作同一行的不同列,他们都影响了这一行的状态(同一个值)[/quote] 谢谢,这样我就清楚了 不能把datatable当二维数组 我还是老老实实每个地方都加锁就好了
layershow 2014-11-06
  • 打赏
  • 举报
回复
引用 8 楼 Z65443344 的回复:
现在是两个线程操作的其实不是同一列,是否也必须加锁?
要加锁,刚才我举那个例子就是,如果操作同一行的不同列,他们都影响了这一行的状态(同一个值)
於黾 2014-11-06
  • 打赏
  • 举报
回复
现在是两个线程操作的其实不是同一列,是否也必须加锁?
於黾 2014-11-06
  • 打赏
  • 举报
回复
引用 6 楼 layershow 的回复:
可能会有问题吧,比如说 DataTable 按行标记是否已经被修改过,每一行有一个状态 写操作必须同步,这个还是要遵守的
写操作同步,是只向同一个单元格里写入要加锁,还是只要写入,不管写到哪里,都必须加锁?
layershow 2014-11-06
  • 打赏
  • 举报
回复
可能会有问题吧,比如说 DataTable 按行标记是否已经被修改过,每一行有一个状态 写操作必须同步,这个还是要遵守的
於黾 2014-11-06
  • 打赏
  • 举报
回复
读取OPC是开了一个线程,一直死循环+sleep去请求数据,然后通过回调函数取值 而TCP是异步侦听,虽然没有显式的使用线程,应该也是另起线程操作的 加上主线程,一共3个线程
於黾 2014-11-06
  • 打赏
  • 举报
回复
是否多线程同时向不同的单元格里写入也会有问题?
於黾 2014-11-06
  • 打赏
  • 举报
回复
引用 2 楼 feiyun0112 的回复:
该类型对于多线程读操作是安全的。您必须使任何写操作同步。
更新"OPC值"这一列,只有一个地方,就是读OPC的回调函数里 而更新"OPC旧值"也只有一个地方,就是客户端请求最新的数据时,比较"OPC值"和"OPC旧值"如果有不同,把数据发送到客户端,同时用"OPC值"更新"OPC旧值" 其他列,是只读不写的,而且不存在多线程向同一行同一列里写入的情况
feiyun0112 2014-11-06
  • 打赏
  • 举报
回复
该类型对于多线程读操作是安全的。您必须使任何写操作同步。
於黾 2014-11-06
  • 打赏
  • 举报
回复
而且这个问题最麻烦的一点就是:没法调试 我在自己电脑上怎么跑,它就是不窜 放到生产环境里去,通过VPN远程观察,经过一段时间之后就窜了 虽然从Unity3D里看不出收到的数据错乱,但是datagridview里显示错乱,实际的datatable应该还是错乱了

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧