单实例.NET核心应用程序(或者让crontab只运行我的应用程序的一个实例)



我想在Linux中使用crontab时间表执行.NET核心应用程序。这是一个长时间运行的操作,如果上一次执行尚未完成,我不希望再运行另一个实例。换句话说,我不希望crontab在给定的时间执行我的.NET核心应用程序的多个实例。

有什么办法可以避免吗?我宁愿不修改我的应用程序的代码。也许crontab有一个避免并发的选项。我还不是Linux专家:)

对于那些想要从代码中检查实例的人,可以使用像这样的命名互斥体

const string mutexName = @"GlobalappName";
var mutex = new Mutex(true, mutexName, out var createdNew);
if (!createdNew)
{
Console.WriteLine(mutexName + " is already running! Exiting the application.");
return;
}

请确保您的互斥对象名称以"Global"开头。

我终于为Raspbian使用了一个小工具:flock

在我的crontab配置文件中,我放置了以下内容:

flock -n /tmp/importer.lock dotnet ~/Desktop/Importer/Plugin.Clm.Importer.Console.dll

看起来flock在运行时编写了一个锁文件,并执行了该命令。如果它再次执行,并且锁定文件在那里,它就会失败。当它完成时,它会释放文件,允许再次调用它。

简而言之:它充当信号灯:)

如果你想在程序中解决它,你可以在System.Diagnostics.Process.GetProcesses中的所有进程中循环,并检查是否有任何进程可执行路径以你的文件名结尾。

[System.STAThread]
static void Main(string[] args)
{
foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
{
if (p.MainModule.FileName.EndsWith("bla.exe", System.StringComparison.CurrentCultureIgnoreCase))
return;
}
[...]
}

否则,让脚本在/var/run中设置一个值,并检查该文件是否存在。如果您的程序以任何方式退出,该文件都将被删除。

此外,还要解决运行时间长的问题。程序通常不会花那么长时间。在我看来,你好像做错了什么,除非你确实在处理几GB的数据。

如果MainModule返回dotnet,您也可以读取链接proc/pid.exe/(Linux或BSD)或/proc/self/exe(仅限Linux)

int pid = System.Diagnostics.Process.GetCurrentProcess().Id;
System.Text.StringBuilder sb = new System.Text.StringBuilder(System.Environment.SystemPageSize);
int ret = Mono.Unix.Native.Syscall.readlink($"/proc/{pid}/exe", sb);
string res = sb.ToString();
System.Console.WriteLine(res);

或者,如果这也只产生dotnet,您可以读取命令行参数(/proc/pid/cmdline-linux only):

public static byte[] ReadReallyAllBytes(string filename)
{
byte[] retValue = null;
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
{
byte[] buffer = new byte[System.Environment.SystemPageSize];
List<byte> byteList = new List<byte>();
int ct = 0;
while ((ct = fs.Read(buffer, 0, buffer.Length)) > 0)
{
for (int i = 0; i < ct; ++i)
{
byteList.Add(buffer[i]);
}
}
buffer = null;
retValue = byteList.ToArray();
byteList.Clear();
byteList = null;
}
return retValue;
}

public static List<string> GetCmdLineArgs(int pid)            
{
List<string> ls = new List<string>();                
byte[] buffer = ReadReallyAllBytes($"/proc/{pid}/cmdline");
int last = 0;
for (int i = 0; i < buffer.Length; ++i)
{
if (buffer[i] == 0)
{
string arg = System.Text.Encoding.UTF8.GetString(buffer, last, i-last);
last = i + 1;
ls.Add(arg);
} // End if (buffer[i] == 0)
} // Next i 
// System.Console.WriteLine(ls);
return ls;
}

现在,如果MainModule是dotnet,请检查命令行参数列表是否包含dll/exe。此外,如果您进行发布构建(独立-没有共享框架),那么它应该与MainModule一起工作。

也许把重复检查放在脚本中,而不是程序中。

因此,在执行程序之前,请检查"ps-ef|grep myprogramname"的输出。

最新更新