Linux下如何检测已执行的.NET控制台应用进程是否运行?
解决Linux下DotNet控制台应用唯一性检测的可行方案
针对Linux下所有dotnet进程名称一致,无法通过进程名判断目标应用是否运行的问题,以下是几个无竞态、跨平台的可靠方案:
方案1:通过进程命令行参数识别
Linux下dotnet进程的命令行参数会包含目标应用的关键标识(比如dotnet run时的项目名,或直接运行dll时的文件路径)。你可以遍历所有dotnet进程,检查其命令行是否包含目标应用的唯一名称(如Bookstore.SendSignUpEmails)。
实现示例(C#):
using System.Diagnostics; using System.IO; bool IsAppRunning(string appIdentifier) { var dotnetProcesses = Process.GetProcessesByName("dotnet"); foreach (var process in dotnetProcesses) { try { // Linux下读取/proc/{pid}/cmdline获取完整命令行(进程参数以\0分隔) var cmdline = File.ReadAllText($"/proc/{process.Id}/cmdline").Replace('\0', ' '); if (cmdline.Contains(appIdentifier)) { return true; } } catch (Exception) { // 无权限访问进程信息时跳过 continue; } } return false; } // 使用方式 if (!IsAppRunning("Bookstore.SendSignUpEmails")) { Process.Start("dotnet", "run Bookstore.SendSignUpEmails"); }
优缺点:无需修改子应用,仅在主应用处理;但依赖命令行参数格式,若子应用启动方式变化(如直接运行dll)需调整匹配逻辑。
方案2:使用命名互斥体(跨平台)
命名互斥体是系统级同步原语,Windows和Linux均支持。每个子应用启动时尝试创建唯一命名的互斥体,若创建失败则说明已有实例运行;主应用通过尝试打开互斥体判断是否存在运行中的实例。
子应用实现:
using System.Threading; class Program { static void Main(string[] args) { // 用应用唯一标识作为互斥体名称,Linux下加"Global/"确保全局可见 using var mutex = new Mutex(true, "Global/Bookstore.SendSignUpEmails_Mutex", out bool createdNew); if (!createdNew) { Console.WriteLine("应用已在运行"); return; } // 执行应用核心逻辑 // ... } }
主应用检测逻辑:
bool IsAppRunning(string appIdentifier) { try { using var mutex = Mutex.OpenExisting($"Global/{appIdentifier}_Mutex"); return true; } catch (WaitHandleCannotBeOpenedException) { // 互斥体不存在,无实例运行 return false; } } // 使用方式 if (!IsAppRunning("Bookstore.SendSignUpEmails")) { Process.Start("dotnet", "run Bookstore.SendSignUpEmails"); }
优缺点:完全避免竞态,跨平台一致性好;需修改每个子应用的启动逻辑,确保互斥体正确创建与释放。
方案3:PID文件+文件独占锁
通过创建PID文件并使用文件独占锁,确保同一时间仅一个实例运行。子应用启动时以独占模式打开PID文件,进程运行期间保持锁定;主应用通过尝试获取文件锁判断是否有实例在运行。
子应用实现:
using System.IO; using System.Diagnostics; class Program { static void Main(string[] args) { // 非root用户可改用用户目录下的路径,避免权限问题 var pidFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".run", "Bookstore.SendSignUpEmails.pid"); Directory.CreateDirectory(Path.GetDirectoryName(pidFilePath)); FileStream pidFileStream = null; try { // 以独占模式打开文件,若已被锁定则抛出异常 pidFileStream = new FileStream( pidFilePath, FileMode.Create, FileAccess.Write, FileShare.None); // 写入当前进程PID using var writer = new StreamWriter(pidFileStream); writer.WriteLine(Process.GetCurrentProcess().Id); pidFileStream = null; // 交由using管理流的生命周期 // 执行应用核心逻辑 // ... } catch (IOException) { Console.WriteLine("应用已在运行"); return; } finally { pidFileStream?.Close(); // 正常退出时删除PID文件 if (File.Exists(pidFilePath)) File.Delete(pidFilePath); } } }
主应用检测逻辑:
bool IsAppRunning(string appIdentifier) { var pidFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".run", $"{appIdentifier}.pid"); try { // 尝试以独占模式打开文件,成功则说明无进程持有锁 using var stream = new FileStream( pidFilePath, FileMode.Open, FileAccess.Read, FileShare.None); return false; } catch (IOException) { // 文件被锁定,说明有进程运行 return true; } catch (FileNotFoundException) { // 文件不存在,无进程运行 return false; } } // 使用方式 if (!IsAppRunning("Bookstore.SendSignUpEmails")) { Process.Start("dotnet", "run Bookstore.SendSignUpEmails"); }
优缺点:符合Linux系统传统做法,无需依赖.NET特定API;需处理文件权限问题,进程异常退出时系统会自动释放锁,但PID文件可能残留(不影响检测逻辑)。
内容的提问来源于stack exchange,提问作者Teoman shipahi
相关产品推荐
相关产品推荐

