【26】C#实战篇—— 多个线程函数对同一个 Excel 文件进行写操作引起的文件冲突问题,解决方法

发布于:2025-08-10 ⋅ 阅读:(21) ⋅ 点赞:(0)

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 确保了锁对象在程序的生命周期内是唯一且不变的。


网站公告

今日签到

点亮在社区的每一天
去签到