# C# 多窗口脚本运行的最佳解决方案
在C# WinForms应用中实现多窗口脚本运行时,有多种成熟的解决方案可以确保系统的稳定性、灵活性和高性能。下面我将详细介绍几种核心方案及其实现细节。
## 1. 基于事件驱动的多窗口通信方案
### 核心架构设计
```csharp
// 脚本执行管理器
public class ScriptExecutionManager
{
private static readonly ScriptExecutionManager _instance = new ScriptExecutionManager();
public static ScriptExecutionManager Instance => _instance;
public event EventHandler<ScriptCompletedEventArgs> ScriptCompleted;
public event EventHandler<ScriptErrorEventArgs> ScriptError;
private readonly Dictionary<string, ScriptContext> _runningScripts = new Dictionary<string, ScriptContext>();
public void ExecuteScript(string scriptName, Form targetWindow, object parameters = null)
{
var context = new ScriptContext
{
ScriptName = scriptName,
TargetWindow = targetWindow,
Parameters = parameters,
StartTime = DateTime.Now
};
_runningScripts[scriptName] = context;
// 异步执行脚本
Task.Run(() => RunScript(context));
}
private void RunScript(ScriptContext context)
{
try
{
// 模拟脚本执行
Thread.Sleep(1000);
// 脚本完成通知
ScriptCompleted?.Invoke(this, new ScriptCompletedEventArgs
{
ScriptName = context.ScriptName,
Result = "执行成功",
ExecutionTime = DateTime.Now - context.StartTime
});
}
catch (Exception ex)
{
ScriptError?.Invoke(this, new ScriptErrorEventArgs
{
ScriptName = context.ScriptName,
ErrorMessage = ex.Message,
StackTrace = ex.StackTrace
});
}
finally
{
_runningScripts.Remove(context.ScriptName);
}
}
}
```
### 窗口间的事件订阅机制
```csharp
// 主窗口
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// 订阅脚本执行事件
ScriptExecutionManager.Instance.ScriptCompleted += OnScriptCompleted;
ScriptExecutionManager.Instance.ScriptError += OnScriptError;
}
private void OnScriptCompleted(object sender, ScriptCompletedEventArgs e)
{
if (InvokeRequired)
{
Invoke(new Action<object, ScriptCompletedEventArgs>(OnScriptCompleted), sender, e);
return;
}
// 更新UI显示执行结果
MessageBox.Show($"脚本 {e.ScriptName} 执行完成,耗时:{e.ExecutionTime.TotalSeconds}秒");
}
private void btnOpenChildForm_Click(object sender, EventArgs e)
{
var childForm = new ChildForm();
childForm.Show();
}
}
// 子窗口
public partial class ChildForm : Form
{
public ChildForm()
{
InitializeComponent();
}
private void btnRunScript_Click(object sender, EventArgs e)
{
// 通过管理器执行脚本
ScriptExecutionManager.Instance.ExecuteScript("数据处理脚本", this, new { DataSource = "数据库" });
}
}
```
## 2. 基于参数化设计的脚本调度系统
### 脚本参数化配置表
| 参数类型 | 配置方式 | 应用场景 | 优势 |
|---------|---------|---------|------|
| 构造函数参数 | 通过构造函数传递初始配置 | 窗口初始化时的固定参数 | 线程安全,生命周期明确 |
| 属性设置 | 运行时动态修改属性值 | 需要灵活调整的配置参数 | 灵活性高,可实时调整 |
| 方法参数 | 执行方法时传入参数 | 单次执行的临时参数 | 隔离性好,无状态影响 |
| 配置文件 | 从外部文件加载配置 | 复杂的企业级配置 | 易于维护,支持热更新 |
### 动态脚本加载实现
```csharp
public class DynamicScriptLoader
{
private readonly string _scriptsDirectory;
private readonly FileSystemWatcher _fileWatcher;
public DynamicScriptLoader(string scriptsPath)
{
_scriptsDirectory = scriptsPath;
// 监控脚本文件变化
_fileWatcher = new FileSystemWatcher(scriptsPath, "*.cs")
{
EnableRaisingEvents = true,
IncludeSubdirectories = false
};
_fileWatcher.Created += OnScriptFileChanged;
_fileWatcher.Changed += OnScriptFileChanged;
_fileWatcher.Deleted += OnScriptFileChanged;
}
private void OnScriptFileChanged(object sender, FileSystemEventArgs e)
{
// 重新加载脚本
LoadScripts();
}
public List<IScript> LoadScripts()
{
var scripts = new List<IScript>();
var scriptFiles = Directory.GetFiles(_scriptsDirectory, "*.cs");
foreach (var file in scriptFiles)
{
try
{
var scriptCode = File.ReadAllText(file);
var script = CompileScript(scriptCode, Path.GetFileNameWithoutExtension(file));
if (script != null)
scripts.Add(script);
}
catch (Exception ex)
{
LogError($"加载脚本 {file} 失败: {ex.Message}");
}
}
return scripts;
}
private IScript CompileScript(string code, string scriptName)
{
// 使用CSharpCodeProvider动态编译脚本
// 具体实现略...
return new DynamicScript(scriptName, code);
}
}
```
## 3. 线程安全与资源管理方案
### 线程安全的脚本执行队列
```csharp
public class ThreadSafeScriptQueue
{
private readonly ConcurrentQueue<ScriptTask> _scriptQueue = new ConcurrentQueue<ScriptTask>();
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
public async Task EnqueueScript(ScriptTask task)
{
_scriptQueue.Enqueue(task);
await ProcessQueue();
}
private async Task ProcessQueue()
{
await _semaphore.WaitAsync();
try
{
while (_scriptQueue.TryDequeue(out var task))
{
if (_cancellationTokenSource.Token.IsCancellationRequested)
break;
await ExecuteScriptTask(task);
}
}
finally
{
_semaphore.Release();
}
}
private async Task ExecuteScriptTask(ScriptTask task)
{
// 跨线程UI更新安全处理
if (task.TargetForm.InvokeRequired)
{
await Task.Run(() =>
{
task.TargetForm.Invoke(new Action(() =>
{
ExecuteScriptOnUIThread(task);
}));
});
}
else
{
ExecuteScriptOnUIThread(task);
}
}
}
```
## 4. 性能优化策略对比表
| 优化维度 | 具体策略 | 预期效果 | 实现复杂度 |
|---------|---------|---------|-----------|
| 内存管理 | 对象池复用脚本执行上下文 | 减少GC压力,提升响应速度 | 中等 |
| 并发控制 | 信号量限制并发脚本数量 | 避免资源竞争,提升稳定性 | 简单 |
| 缓存策略 | 预编译常用脚本 | 减少编译开销,提升执行速度 | 中等 |
| 异步处理 | 使用async/await模式 | 避免UI线程阻塞,提升用户体验 | 简单 |
| 资源释放 | 实现IDisposable接口 | 及时释放资源,防止内存泄漏 | 简单 |
## 5. 完整的错误处理与日志系统
```csharp
public class ScriptErrorHandler
{
private readonly ILogger _logger;
public ScriptErrorHandler(ILogger logger)
{
_logger = logger;
}
public bool HandleScriptError(Exception ex, string scriptName, Form targetWindow)
{
try
{
// 记录错误日志
_logger.Error($"脚本 {scriptName} 执行失败: {ex.Message}", ex);
// 根据错误类型采取不同策略
if (ex is OutOfMemoryException)
{
// 内存不足处理
HandleMemoryError(targetWindow);
return false;
}
else if (ex is TimeoutException)
{
// 超时重试
return RetryScript(scriptName, targetWindow);
}
else
{
// 一般错误,显示给用户
ShowErrorMessage(targetWindow, ex.Message);
return true;
}
}
catch (Exception handlerEx)
{
// 错误处理器自身出错
_logger.Fatal("错误处理器发生异常", handlerEx);
return false;
}
}
private void ShowErrorMessage(Form targetWindow, string message)
{
if (targetWindow.InvokeRequired)
{
targetWindow.Invoke(new Action<Form, string>(ShowErrorMessage), targetWindow, message);
return;
}
MessageBox.Show(targetWindow, message, "脚本执行错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
```
## 6. 实际应用场景示例
### 医疗数据交互系统案例
```csharp
// 医疗数据处理脚本
public class MedicalDataScript : IScript
{
public string ScriptName => "医疗数据处理";
public async Task<ScriptResult> ExecuteAsync(object parameters)
{
var medicalParams = parameters as MedicalScriptParameters;
if (medicalParams == null)
throw new ArgumentException("参数类型不正确");
// 数据验证
if (string.IsNullOrEmpty(medicalParams.PatientId))
throw new ArgumentException("患者ID不能为空");
// 异步处理医疗数据
var result = await ProcessMedicalData(medicalParams);
// 更新多个相关窗口
UpdateRelatedWindows(result);
return new ScriptResult { Success = true, Data = result };
}
private void UpdateRelatedWindows(MedicalDataResult result)
{
// 查找所有需要更新的医疗相关窗口
var medicalWindows = Application.OpenForms
.OfType<IMedicalDataReceiver>()
.ToList();
foreach (var window in medicalWindows)
{
window.ReceiveMedicalData(result);
}
}
}
```
通过以上方案的综合运用,可以在C# WinForms应用中构建出稳定、高效的多窗口脚本运行系统。关键是要根据具体业务需求选择合适的通信机制、确保线程安全、实现完善的错误处理,并进行适当的性能优化[ref_2][ref_3]。