使用static来避免“重复读”

本文介绍了一种在Web程序开发中避免重复读取数据的方法。通过使用静态变量和函数结果缓存,可以在同一线程内有效减少不必要的数据加载,从而提高程序效率。
在较复杂的web程序开发中,由于采用面向对象的数据操作方式,或者业务逻辑太过复杂,开发人员在开发过程中,经常会无意识的重复读取数据。
比如:
$result1 = tableobjectPeer::getResult($var1,$var2,$var3);
开发人员在需要的该table数据的时候,经常直接调用 tableobjectPeer::getResult方法。
又或者程序在进行forward()的时候,也会导致重复调用  tableobjectPeer::getResult 方法,导致“重复读”。

要避免类似的“重复读”,最主要的办法是,开发人员在代码开发的时候,就要有“避免重复读”的意识。
其实只要在:
$result1 = tableobjectPeer::getResult($var1,$var2,$var3);
之后,需要的地方进行
$result2 = $result1;
$result3 = $result1;
即可。这样子就可以避免掉大量的“重复读”了。
但是如果开放人员一开始没有这样做,那要进行这方面的重构可能是很大的工作量。
另外,由于框架中的forward()也容易造成“重复读”。如果“重复读”是forward()造成的,那该方法就不可行了(这点也许和不同的开发语言,不同的开发框架有关,在php的symfony框架中是如此)。

因此,在采用上面的方法进行优化的同时,对于一些较复杂的情况,决定采用另一种方式:使用static,设置变量为静态变量,来避免重复读取数据

示例代码如下:
ContractedBlock.gifExpandedBlockStart.gifCode
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><?php
//要改写的函数也请加上$is_static=1变量,用来控制是否开启 static。
function staticFunc ($var1,$var2,$var3,$is_static=1)
{
    
if ( $is_static == 1 )//默认需要缓存函数结果
    {
        
static $result_array;//该数组用来保存函数的结果,支持不同参数的结果缓存
        $vars_string = serializefunc_get_args() );

        
if ( empty$result_array ) )//第一次运行需要初始化
        {
            
$result_array = array();
        }

        
if ( array_key_exists$vars_string, $result_array ) )//参数已经存在
        {
            
return $result_array[$vars_string];//返回静态变量中已经保存的结果
        }else//参数不存在
        {
            
$result_array[$vars_string= '';//后面会把结果补充进来
        }
    }
else//不利用static 缓冲结果
    {
        
if ( empty$result_array ) )
        {
            
$result_array = array();
        }
    }

    
$result_array[$vars_string= rand();//获取结果,请把获取的代码放在此处即可
    return $result_array[$vars_string];
}

echo staticFunc(1,2,3);
echo "<br>";
echo staticFunc(2,2,2);
echo "<br>";
echo staticFunc(1,2,3);
echo "<br>";
echo staticFunc(2,2,2);
echo "<br>";
echo staticFunc(3,3,3);
echo "<br>";
echo staticFunc(3,3,3,0);
echo "<br>";
?> 
运行上述代码,产生的结果类似:
16667
8888
16667
8888
2193
1014
可以看出,第1行和第3行的结果一致,第2行和第4行的结果一致,说明只要函数的参数相同,函数结果就进行了有效的“cache”。
从第4行和第5行,可以看出,通过设置$is_static变量能有效控制是否开启“cache”。

补充:上述使用static方法,可以有效的避免在一个线程内,重复读取数据,但是该cache只存在一个线程内,不同线程是相互独立的。虽然只是线程内的函数结果“cache”,但是其原理和其他方式的cache是类似的,就是要针对不同的参数(不同的情况)构造出cache的key。
<think>我们面对的问题:在多线程环境中使用bool变量防止重复文件。 根据引用[1]和[2],bool变量在多线程中不是线程安全的,因为存在可见性和编译器优化问题。 解决方案: 1. 使用锁(lock)来确保只有一个线程可以进入临界区,从而安全地检查和设置bool变量。 2. 使用双重检查锁定(Double-Check Locking)来减少锁的开销。 3. 使用Interlocked类提供的原子操作来更新bool变量(但Interlocked没有直接针对bool的操作,我们可以用int代替,0表示false,1表示true)。 4. 使用volatile关键字确保变量的可见性(但volatile不能完全解决原子性问题,因此通常需要配合其他机制)。 我们将采用双重检查锁定模式,因为: - 第一次不加锁检查可以提高性能(如果已经取则直接返回) - 第二次加锁检查确保只有一个线程执行取操作 另外,为了演示文件取,我们假设取一个文本文件,并将内容存储在一个静态变量中,以便后续使用。 注意:如果文件内容可能改变,我们需要考虑文件更新的情况(例如通过文件修改时间戳),但题目要求是防止重复,所以我们只一次。 步骤: 1. 声明一个静态的bool变量(初始为false)来表示文件是否已。 2. 声明一个静态对象作为锁对象。 3. 声明一个静态变量来存储取的文件内容(如果需要)。 代码示例: 我们将创建一个类,该类有一个方法用于取文件,并且确保在多线程环境下只取一次。 注意:如果文件不存在等异常情况,我们也要处理。如果取失败,我们可能希望下次再次尝试取,因此我们可以在取失败时将标志重置为false(但这里为了简单,我们只处理取成功的情况)。 以下代码示例使用了双重检查锁定模式。 </think>### 在 C# 中使用线程和 bool 型变量防止重复文件的实现方案 在多线程环境中防止重复取文件需要特殊处理,因为标准的 bool 变量**不具有线程安全性**(引用[^1])。以下是结合线程同步机制和 bool 变量的完整解决方案: #### 线程安全的实现代码 ```csharp using System; using System.IO; using System.Threading; namespace ThreadSafeFileReader { class FileLoader { // 使用 volatile 确保多线程可见性 private volatile bool _isFileLoaded = false; // 文件内容缓存 private string _cachedContent = null; // 同步锁对象 private readonly object _lockObject = new object(); // 文件监视器 private FileSystemWatcher _fileWatcher; public string FilePath { get; } public FileLoader(string filePath) { FilePath = filePath; // 设置文件监视器 SetupFileWatcher(); } // 带线程安全的文件加载方法 public string LoadContent() { // 第一重检查(无锁) if (_isFileLoaded) return _cachedContent; lock (_lockObject) // 进入临界区 { // 第二重检查(带锁) if (_isFileLoaded) return _cachedContent; try { Console.WriteLine($"[Thread {Thread.CurrentThread.ManagedThreadId}] 首次加载文件"); _cachedContent = File.ReadAllText(FilePath); _isFileLoaded = true; return _cachedContent; } catch (Exception ex) { Console.WriteLine($"文件取失败: {ex.Message}"); return null; } } } // 设置文件监视器(引用[^3]) private void SetupFileWatcher() { string directory = Path.GetDirectoryName(FilePath); string fileName = Path.GetFileName(FilePath); _fileWatcher = new FileSystemWatcher { Path = directory, Filter = fileName, NotifyFilter = NotifyFilters.LastWrite }; _fileWatcher.Changed += (sender, e) => { lock (_lockObject) { Console.WriteLine("检测到文件修改,重置加载状态"); _isFileLoaded = false; // 文件更新时重置状态 } }; _fileWatcher.EnableRaisingEvents = true; } } class Program { static void Main() { var loader = new FileLoader("data.txt"); // 模拟多线程访问 Thread[] threads = new Thread[5]; for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(() => { string content = loader.LoadContent(); Console.WriteLine($"[Thread {Thread.CurrentThread.ManagedThreadId}] 内容长度: {content?.Length}"); }); threads[i].Start(); Thread.Sleep(50); // 模拟延迟 } foreach (var thread in threads) thread.Join(); } } } ``` #### 关键技术解析 1. **双重检查锁定模式** - 第一重无锁检查避免不必要的锁竞争 - 第二重带锁检查确保线程安全 - 使用 `lock(_lockObject)` 保证临界区原子性 2. **volatile 关键字** ```csharp private volatile bool _isFileLoaded; ``` - 解决可见性问题:强制所有线程取最新值 - 禁止编译器优化缓存(引用[^1]) 3. **文件更新监控** - 使用 `FileSystemWatcher` 监听文件变更 - 文件修改时自动重置 `_isFileLoaded = false` - 确保下次取获取最新内容 4. **线程同步机制** - `lock` 语句替代 `AutoResetEvent`(更简单高效) - 避免引用[^4]中提到的顺序执行问题 #### 执行流程说明 1. 首次调用时,一个线程进入临界区取文件 2. 后续线程直接返回缓存内容 3. 文件修改后重置状态,触发重新取 4. 控制台输出示例: ``` [Thread 4] 首次加载文件 [Thread 4] 内容长度: 1250 [Thread 5] 内容长度: 1250 [Thread 6] 内容长度: 1250 检测到文件修改,重置加载状态 [Thread 7] 首次加载文件 [Thread 7] 内容长度: 980 ``` #### 适用场景 - 配置文件加载 - 静态资源缓存 - 频繁访问的只数据 - 高并发服务中的资源初始化(引用[^2]) > **注意事项**:对于超大型文件(>1GB),建议改用流式避免内存溢出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值