局域网多机通过VSTO同时访问共享XML文件的解决方案咨询
局域网多进程共享XML文件写入方案(无异步/队列/数据库)
核心思路
针对20台设备同时写入共享XML的场景,用文件排他锁+后台线程重试的方案,既保证文件操作的原子性,又不会阻塞Word主程序的UI线程。
关键实现建议
- 排他锁控制并发:打开文件时使用
FileShare.None参数,确保同一时间只有一个进程能写入文件,其他进程触发异常后通过重试机制等待锁释放。 - 后台线程执行写入:把文件写入逻辑放到后台线程(比如
ThreadPool),避免阻塞Word的UI操作,满足不阻塞程序的需求。 - 缩短锁持有时间:尽量快速完成XML的读取、修改、写入操作,减少其他进程等待时长,降低冲突概率。
- 本地缓存兜底:如果多次重试仍写入失败,将待写入的数据暂存到本地临时文件,下次Word启动时再尝试同步到共享XML,避免数据丢失。
C#代码片段(适配VSTO加载项)
using System; using System.IO; using System.Threading; using System.Xml.Linq; namespace WordVSTOAddIn { public class SharedXmlWriter { private const string SharedXmlPath = @"\\YourServer\SharedFolder\Stats.xml"; private const int MaxRetryCount = 5; private const int RetryDelayMs = 1000; // 每次重试间隔1秒 // 调用此方法提交统计数据,不会阻塞Word主线程 public void SubmitStatsAsync(string deviceId, string statsData) { // 用ThreadPool把写入逻辑放到后台线程 ThreadPool.QueueUserWorkItem(state => { var stats = (Tuple<string, string>)state; WriteStatsToSharedXml(stats.Item1, stats.Item2); }, Tuple.Create(deviceId, statsData)); } private void WriteStatsToSharedXml(string deviceId, string statsData) { int retryCount = 0; bool writeSuccess = false; while (retryCount < MaxRetryCount && !writeSuccess) { try { XDocument xmlDoc; // 打开或创建XML文件,使用排他锁 using (var fs = new FileStream(SharedXmlPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) { if (fs.Length == 0) { // 文件为空,初始化根节点 xmlDoc = new XDocument(new XElement("DeviceStats")); } else { xmlDoc = XDocument.Load(fs); } // 添加新的统计节点 var newStat = new XElement("DeviceStat", new XAttribute("DeviceId", deviceId), new XAttribute("Timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")), new XElement("Data", statsData)); xmlDoc.Root.Add(newStat); // 回到文件开头,覆盖写入 fs.Seek(0, SeekOrigin.Begin); xmlDoc.Save(fs); // 截断多余的字节(如果文件原来更长) fs.SetLength(fs.Position); writeSuccess = true; } } catch (IOException) { // 捕获文件被占用的异常,重试 retryCount++; Thread.Sleep(RetryDelayMs); } catch (Exception ex) { // 其他异常,比如XML格式错误,记录日志并退出 File.AppendAllText(@"C:\Temp\XmlWriteError.log", $"{DateTime.Now}: {ex.Message}\r\n"); break; } } if (!writeSuccess) { // 多次重试失败,缓存到本地临时文件 var tempPath = Path.Combine(Path.GetTempPath(), $"StatsCache_{deviceId}_{Guid.NewGuid()}.xml"); var cacheDoc = new XDocument(new XElement("CachedStat", new XAttribute("DeviceId", deviceId), new XAttribute("Timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")), new XElement("Data", statsData))); cacheDoc.Save(tempPath); } } // 可选:Word启动时同步本地缓存到共享XML public void SyncCachedStatsOnStartup() { var tempDir = Path.GetTempPath(); var cacheFiles = Directory.GetFiles(tempDir, "StatsCache_*.xml"); foreach (var file in cacheFiles) { try { var cacheDoc = XDocument.Load(file); var deviceId = cacheDoc.Root.Attribute("DeviceId")?.Value; var statsData = cacheDoc.Root.Element("Data")?.Value; if (!string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(statsData)) { WriteStatsToSharedXml(deviceId, statsData); File.Delete(file); // 同步成功后删除缓存文件 } } catch (Exception ex) { File.AppendAllText(@"C:\Temp\CacheSyncError.log", $"{DateTime.Now}: {ex.Message}\r\n"); } } } } }
使用说明
- 把
SharedXmlPath替换为你的共享文件夹XML路径。 - 在VSTO加载项的
ThisAddIn_Startup方法里,调用SyncCachedStatsOnStartup()同步本地缓存。 - 需要提交统计数据时,调用
SubmitStatsAsync(deviceId, statsData),比如在Word关闭事件或者某个操作完成后。
内容的提问来源于stack exchange,提问作者deku
相关产品推荐
相关产品推荐

