1 问题描述
现在cam1Func ~ cam5Func共5个相机测试的功能函数,C#开启5个线程同时运行这5个camFunc函数,
这5个camFunc都要调用 同一个名为 “Data_P0.xlsx"的Excel文件
, 那么这就导致Data_P0.xlsx文件冲突。
string m_ExcelName = "../Data/Data_P0.xlsx";
public void Cam1Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[0])
{
m_Excel_Helper.SaveData(m_ExcelName, "cam1", "B6", BackVal, Variance, LWidth, spacingRows);
isSaveData[0] = true;
}
// ...
}
public void Cam2Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[1])
{
m_Excel_Helper.SaveData(m_ExcelName, "cam2", "B4", BackVal, Variance, LWidth, spacingRows);
isSaveData[1] = true;
}
// ...
}
public void Cam3Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[2])
{
m_Excel_Helper.SaveData(m_ExcelName, "cam3", "B2", BackVal, Variance, LWidth, spacingRows);
isSaveData[2] = true;
}
// ...
}
public void Cam4Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[3])
{
lock (excelLock)
{
m_Excel_Helper.SaveData(m_ExcelName, "cam4", "F6", BackVal, Variance, LWidth, spacingRows);
}
isSaveData[3] = true;
}
// ...
}
public void Cam5Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[4])
{
m_Excel_Helper.SaveData(m_ExcelName, "cam5", "F2", BackVal, Variance, LWidth, spacingRows);
isSaveData[4] = true;
}
// ...
}
2 锁机制确保对Excel文件的写操作是线程安全的
为了解决同时对同一个 Excel 文件进行写操作引起的文件冲突问题,可以使用锁(lock)机制来确保每次只有一个线程能够访问文件。这样可以避免并发写入时的冲突。
可以使用 C# 中的 lock 关键字来实现这一点。下面是修改后的代码示例:
在类的开头定义这个静态锁对象,这个对象 是 Cam1Func 到 Cam5Func共用的;
// 定义一个静态对象用于锁
private static readonly object excelLock = new object();
然后在每个相机函数中使用这个锁对象来确保写操作是线程安全的。例如:
string m_ExcelName = "../Data/Data_P0.xlsx";
public void Cam1Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[0])
{
// 使用锁机制确保对Excel文件的写操作是线程安全的,避免多线程并发写入时的冲突
lock (excelLock)
{
m_Excel_Helper.SaveData(m_ExcelName, "cam1", "B6", BackVal, Variance, LWidth, spacingRows);
}
isSaveData[0] = true;
}
// ...
}
public void Cam2Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[1])
{
lock (excelLock)
{
m_Excel_Helper.SaveData(m_ExcelName, "cam2", "B4", BackVal, Variance, LWidth, spacingRows);
}
isSaveData[1] = true;
}
// ...
}
public void Cam3Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[2])
{
lock (excelLock)
{
m_Excel_Helper.SaveData(m_ExcelName, "cam3", "B2", BackVal, Variance, LWidth, spacingRows);
}
isSaveData[2] = true;
}
// ...
}
public void Cam4Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[3])
{
lock (excelLock)
{
m_Excel_Helper.SaveData(m_ExcelName, "cam4", "F6", BackVal, Variance, LWidth, spacingRows);
}
isSaveData[3] = true;
}
// ...
}
public void Cam5Func(bool isProcess, CancellationToken cancellationToken)
{
// 类似的代码结构
// ...
if (LWidth[0] != 0 && !isSaveData[4])
{
lock (excelLock)
{
m_Excel_Helper.SaveData(m_ExcelName, "cam5", "F2", BackVal, Variance, LWidth, spacingRows);
}
isSaveData[4] = true;
}
// ...
}
3 为什么是readonly?不是要写数据吗?
readonly 关键字用于确保 excelLock 对象在创建之后不能被更改。这是一个非常好的实践,因为锁对象不需要改变,只需要在多个线程之间共享它。将锁对象定义为 readonly 可以确保在整个应用程序运行期间它的引用不会被意外改变,从而避免潜在的线程安全问题。
与写/读数据无关,即使你要写数据,锁对象本身并不会存储数据或执行写操作。锁对象只是用于同步对资源的访问,确保每次只有一个线程能够访问该资源
。在这种情况下,readonly 确保了锁对象在程序的生命周期内是唯一且不变的。