Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# linux上的单实例dotnetcore cli应用程序_C#_Ubuntu_.net Core - Fatal编程技术网

C# linux上的单实例dotnetcore cli应用程序

C# linux上的单实例dotnetcore cli应用程序,c#,ubuntu,.net-core,C#,Ubuntu,.net Core,我感兴趣的是如何为dotnetcore控制台应用程序提供单实例策略。令我惊讶的是,似乎没有太多关于这个话题的内容。我找到了这个stacko,但它似乎不适合我使用ubuntu的dotnetcore。这里有人曾经这样做过吗?关于@MusuNaji解决方案的变体,网址: 由于Linux/MacOS上的互斥体检查问题(如上所述),这在.NET core上比应该的要困难一些。此外,Youth的解决方案也没有帮助,因为所有.NET core应用程序都是通过CLI运行的,CLI的进程名为“dotnet”,如果

我感兴趣的是如何为dotnetcore控制台应用程序提供单实例策略。令我惊讶的是,似乎没有太多关于这个话题的内容。我找到了这个stacko,但它似乎不适合我使用ubuntu的dotnetcore。这里有人曾经这样做过吗?

关于@MusuNaji解决方案的变体,网址:


由于Linux/MacOS上的互斥体检查问题(如上所述),这在.NET core上比应该的要困难一些。此外,Youth的解决方案也没有帮助,因为所有.NET core应用程序都是通过CLI运行的,CLI的进程名为“dotnet”,如果在同一台机器上运行多个.NET core应用程序,则会错误触发重复实例检查

实现这一点的一种简单方法也是多平台健壮的,即在应用程序启动时打开一个文件进行写入,并在最后关闭它。如果文件无法打开,则是由于另一个实例同时运行,您可以在try/catch中处理该问题。如果文件不存在,使用FileStream打开文件也会创建它

            try
            {
                lockFile = File.OpenWrite("SingleInstance.lck");
            }
            catch (Exception)
            {
                Console.WriteLine("ERROR - Server is already running. End that instance before re-running. Exiting in 5 seconds...");
                System.Threading.Thread.Sleep(5000);
                return;
            }

下面是我使用命名管道的实现。它支持从第二个实例传递参数

注意:我没有在Linux或Mac上进行测试,但理论上应该可以

用法


        public static int Main(string[] args)
        {
            instanceManager = new SingleInstanceManager("8A3B7DE2-6AB4-4983-BBC0-DF985AB56703");
            if (!instanceManager.Start())
            {
                return 0; // exit, if same app is running
            }
            instanceManager.SecondInstanceLaunched += InstanceManager_SecondInstanceLaunched;
            // Initialize app. Below is an example in WPF.
            app = new App();
            app.InitializeComponent();
            return app.Run();
        }
        private static void InstanceManager_SecondInstanceLaunched(object sender, SecondInstanceLaunchedEventArgs e)
        {
            app.Dispatcher.Invoke(() => new MainWindow().Show());
        }

    public class SingleInstanceManager
    {
        private readonly string applicationId;
        public SingleInstanceManager(string applicationId)
        {
            this.applicationId = applicationId;
        }
        /// <summary>
        /// Detect if this is the first instance. If it is, start a named pipe server to listen for subsequent instances. Otherwise, send <see cref="Environment.GetCommandLineArgs()"/> to the first instance.
        /// </summary>
        /// <returns>True if this is tthe first instance. Otherwise, false.</returns>
        public bool Start()
        {
            using var client = new NamedPipeClientStream(applicationId);
            try
            {
                client.Connect(0);
            }
            catch (TimeoutException)
            {
                Task.Run(() => StartListeningServer());
                return true;
            }
            var args = Environment.GetCommandLineArgs();
            using (var writer = new BinaryWriter(client, Encoding.UTF8))
            {
                writer.Write(args.Length);
                for (int i = 0; i < args.Length; i++)
                {
                    writer.Write(args[i]);
                }
            }
            return false;
        }
        private void StartListeningServer()
        {
            var server = new NamedPipeServerStream(applicationId);
            server.WaitForConnection();
            using (var reader = new BinaryReader(server, Encoding.UTF8))
            {
                var argc = reader.ReadInt32();
                var args = new string[argc];
                for (int i = 0; i < argc; i++)
                {
                    args[i] = reader.ReadString();
                }
                SecondInstanceLaunched?.Invoke(this, new SecondInstanceLaunchedEventArgs { Arguments = args });
            }
            StartListeningServer();
        }
        public event EventHandler<SecondInstanceLaunchedEventArgs> SecondInstanceLaunched;
    }
    public class SecondInstanceLaunchedEventArgs
    {
        public string[] Arguments { get; set; }
    }
您的复制和粘贴代码


        public static int Main(string[] args)
        {
            instanceManager = new SingleInstanceManager("8A3B7DE2-6AB4-4983-BBC0-DF985AB56703");
            if (!instanceManager.Start())
            {
                return 0; // exit, if same app is running
            }
            instanceManager.SecondInstanceLaunched += InstanceManager_SecondInstanceLaunched;
            // Initialize app. Below is an example in WPF.
            app = new App();
            app.InitializeComponent();
            return app.Run();
        }
        private static void InstanceManager_SecondInstanceLaunched(object sender, SecondInstanceLaunchedEventArgs e)
        {
            app.Dispatcher.Invoke(() => new MainWindow().Show());
        }

    public class SingleInstanceManager
    {
        private readonly string applicationId;
        public SingleInstanceManager(string applicationId)
        {
            this.applicationId = applicationId;
        }
        /// <summary>
        /// Detect if this is the first instance. If it is, start a named pipe server to listen for subsequent instances. Otherwise, send <see cref="Environment.GetCommandLineArgs()"/> to the first instance.
        /// </summary>
        /// <returns>True if this is tthe first instance. Otherwise, false.</returns>
        public bool Start()
        {
            using var client = new NamedPipeClientStream(applicationId);
            try
            {
                client.Connect(0);
            }
            catch (TimeoutException)
            {
                Task.Run(() => StartListeningServer());
                return true;
            }
            var args = Environment.GetCommandLineArgs();
            using (var writer = new BinaryWriter(client, Encoding.UTF8))
            {
                writer.Write(args.Length);
                for (int i = 0; i < args.Length; i++)
                {
                    writer.Write(args[i]);
                }
            }
            return false;
        }
        private void StartListeningServer()
        {
            var server = new NamedPipeServerStream(applicationId);
            server.WaitForConnection();
            using (var reader = new BinaryReader(server, Encoding.UTF8))
            {
                var argc = reader.ReadInt32();
                var args = new string[argc];
                for (int i = 0; i < argc; i++)
                {
                    args[i] = reader.ReadString();
                }
                SecondInstanceLaunched?.Invoke(this, new SecondInstanceLaunchedEventArgs { Arguments = args });
            }
            StartListeningServer();
        }
        public event EventHandler<SecondInstanceLaunchedEventArgs> SecondInstanceLaunched;
    }
    public class SecondInstanceLaunchedEventArgs
    {
        public string[] Arguments { get; set; }
    }

似乎在macOS上使用命名互斥还不够(只是测试了一下)。您可以尝试使用某种类型的pidfile,只需确保在主进程退出时始终删除该文件。是的,我以前想过,但我希望有更好的方法。现在我想了想,如果您的进程名称不唯一,这将无法正常工作。因此,这是这个解决方案的先决条件。仍然可以使用100%可靠的方法来增强单实例策略。我想在我的例子中,实现单实例策略的最佳方法是将其设置为linux守护进程。我认为至少在upstart中,单实例是我的默认值。我不认为这会很好地工作,因为所有的.net core进程名称都是“netcore”(无论如何在2.x中),这是CLI,而不是您的特定应用程序名称,这意味着任何.NET core应用程序都将触发对进程名称的测试。更正dotnet core进程名称为dotnet而不是netcore。请参阅上面的我的答案,以获得一个简单的替代方案,该方案应该会更好。您断言所有netcore应用程序都是通过dotnet CLI运行的,这是不正确的,尽管您指出从CLI运行不会正确地与我的解决方案配合使用,这是好事。构建自包含应用程序并在dotnet CLI之外执行该应用程序时,它与可执行文件具有相同的名称。如果应用程序在不关闭流的情况下崩溃,会发生什么情况?它能保持打开状态吗?是的,我正在通过Visual Studio进行测试,如果使用自包含的应用程序运行,您关于名称更改的说法是正确的。此外,崩溃应用程序Windows将关闭流(测试正常),但尚未在Linux上尝试此操作。