c# - 在同一程序的多个实例之间同步

标签 c# multithreading single-instance

我手上有一个相当复杂的编程问题,请耐心等待几分钟。

我决定要在 WPF (C#) 中创建一个媒体播放器,但遇到了一些困难。

我希望我的应用程序是单实例的,这样当用户双击服务器文件时,程序只会运行一次并将所有文件排队等待播放。

我尝试了几种方法来实现它,包括微软的单实例实现,但似乎没有任何效果,直到我决定创建我自己的,就像我想的那样并实现它(这可能也在互联网上的某个地方,但没有出现)

基本上,我使用命名的互斥锁来防止打开多个实例,并强制其他实例将它们的参数写入文件,之后,创建互斥锁的实例将读取该文件。 不用说,就性能而言,这是非常非常低效的,但无论如何,这是我对 Main() 函数的实现。 请注意,这个 Main() 也是从头开始编写的,因为我不太喜欢 VS2010 自动生成的那个。

static void Main(string[] args)
    {

            string[] arguments = new string[0];
            handler g = new handler();
            bool createdNew = false;
            Mutex lolpaca = new Mutex(true, "lolpacamaximumtrolololololol", out createdNew);
            if (createdNew)
            {

                if (args != null)
                {
                    var MainWindow = new MainWindow();
                    var app = new Application();
                    app.Run(MainWindow);
                    lolpaca.ReleaseMutex();
                    lolpaca.Dispose();
                }
                else
                {
                            Array.Resize(ref arguments, 1);
                            arguments[0] = args[0];
                            string line;
                    //nu mai arunca exceptii nenorocitule

                            while ((line = g.ReadArgs()) != null)
                            {
                                int old_size = arguments.Length;
                                Array.Resize(ref arguments, arguments.Length + 1);
                                arguments[old_size] = line;
                            }


                    var MainWindow = new MainWindow(arguments, arguments.Length);
                    var app = new Application();
                    app.Run(MainWindow);
                    lolpaca.ReleaseMutex();
                    lolpaca.Dispose();

                }
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
            }

            else
            {
                Thread writer = new Thread(new ParameterizedThreadStart(g.WriteArg));
                writer.Start(args);
                writer.Join();

                 try
                {
                    g.WriteArg(args);
                }
                catch (IOException e)
                {
                    MediaPlayerFinal_GUI_new.ExceptionCatcher exp = new MediaPlayerFinal_GUI_new.ExceptionCatcher(e.Source);
                    exp.Show();
                }

            }

    }

我也在使用这个类来尝试在线程之间同步

   public class handler
{  
    static string path = @"D:\playlist.txt";
    static FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
    string line;

    string arg;
    bool readerFlag = false;
    public string ReadArgs()
    {
        try
        {
            lock (fs)   // Enter synchronization block
            {
                if (!readerFlag)
                {            // Wait until writer  finishes
                    try
                    {
                        // Waits for the Monitor.Pulse in WriteArg
                        Monitor.Wait(fs);
                    }
                    catch (SynchronizationLockException)
                    {

                    }
                    catch (ThreadInterruptedException)
                    {

                    }
                }


                TextReader tr = new StreamReader(fs);
                while ((line = tr.ReadLine()) != null)
                {
                    arg = line;
                }
                tr.Close();
                tr.Dispose();

            }

          /*  fs.Close();
            fs.Dispose();*/
            readerFlag = false;
            Monitor.Pulse(fs);
            return arg;
        }
        catch (IOException e)
        {
            MediaPlayerFinal_GUI_new.ExceptionCatcher exp = new MediaPlayerFinal_GUI_new.ExceptionCatcher(e.Source);
            exp.Show();
            return null;
        }
    }
    public void WriteArg(object args)
    {
        lock (fs)
        {
            try
            {
                if (readerFlag)
                {
                    try
                    {
                        Monitor.Wait(fs);   // Wait for the Monitor.Pulse in ReadArgs
                    }
                    catch (SynchronizationLockException)
                    {

                    }
                    catch (ThreadInterruptedException)
                    {

                    }
                }
                arg = Convert.ToString(args);
                //   FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read);                
                TextWriter tw = new StreamWriter(fs);
                tw.WriteLine(args);
                tw.Close();
                tw.Dispose();


            }
            catch (IOException e)
            {
                MediaPlayerFinal_GUI_new.ExceptionCatcher exp = new MediaPlayerFinal_GUI_new.ExceptionCatcher(e.Source);
                exp.Show();
            }
        }
       /* fs.Close();
        fs.Dispose();*/
        readerFlag = true;
        Monitor.Pulse(fs);
    }

现在,基本上,对于每个双击的文件,Windows 都会创建一个 Main() 函数的实例。 第一个实例控制了互斥锁并继续做它想做的任何事情。 其他实例必须将它们的参数写入文件。

现在,问题是: 显然,线程(所有线程)没有正确同步,有时我会遇到 IO 异常。 我不知道这些异常到底在哪里被抛出,因为 try-catch block 似乎什么都不做。事实上,我相信这比 try-catch 的工作要深入一些。

那么,当用户双击大量文件时,我该如何同步产生的所有线程?此实现适用于最多 3 个双击文件,有时(注意,有时有效,有时无效)超过 3 个文件(测试最多 9 个)。 到目前为止,我在 Internet 上没有找到任何独立运行的同一应用程序的多个实例。

如果你能给我一个例子就太好了:)

谢谢。

最佳答案

在同一应用程序的两个实例之间进行通信的最佳方式是使用 IPC。下面是可用于帮助处理单个实例的类示例:

    /// <summary>
        /// Enforces single instance for an application.
        /// </summary>
        public class SingleInstance : IDisposable
        {
            #region Fields

            /// <summary>
            /// The synchronization context.
            /// </summary>
            private readonly SynchronizationContext synchronizationContext;

            /// <summary>
            /// The disposed.
            /// </summary>
            private bool disposed;

            /// <summary>
            /// The identifier.
            /// </summary>
            private Guid identifier = Guid.Empty;

            /// <summary>
            /// The mutex.
            /// </summary>
            private Mutex mutex;

            #endregion

            #region Constructors and Destructors

            /// <summary>
            /// Initializes a new instance of the <see cref="SingleInstance"/> class.
            /// </summary>
            /// <param name="identifier">
            /// An identifier unique to this application.
            /// </param>
            /// <param name="args">
            /// The command line arguments.
            /// </param>
            public SingleInstance(Guid identifier, IEnumerable<string> args)
            {
                this.identifier = identifier;

                bool ownsMutex;
                this.mutex = new Mutex(true, identifier.ToString(), out ownsMutex);

                this.synchronizationContext = SynchronizationContext.Current;

                this.FirstInstance = ownsMutex;

                if (this.FirstInstance)
                {
                    this.ListenAsync();
                }
                else
                {
                    this.NotifyFirstInstance(args);
                }
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="SingleInstance"/> class.
            /// </summary>
            /// <param name="identifier">
            /// An identifier unique to this application.
            /// </param>
            public SingleInstance(Guid identifier)
                : this(identifier, null)
            {
            }

            #endregion

            #region Public Events

            /// <summary>
            /// Event raised when arguments are received from successive instances.
            /// </summary>
            public event EventHandler<OtherInstanceCreatedEventArgs> OtherInstanceCreated;

            #endregion

            #region Public Properties

            /// <summary>
            /// Gets a value indicating whether this is the first instance of this application.
            /// </summary>
            public bool FirstInstance { get; private set; }

            #endregion

            #region Implemented Interfaces

            #region IDisposable

            /// <summary>
            /// The dispose.
            /// </summary>
            public void Dispose()
            {
                this.Dispose(true);
                GC.SuppressFinalize(this);
            }

            #endregion

            #endregion

            #region Methods

            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            /// <param name="disposing">
            /// True if managed resources should be disposed; otherwise, false.
            /// </param>
            protected virtual void Dispose(bool disposing)
            {
                if (this.disposed)
                {
                    return;
                }

                if (disposing)
                {
                    if (this.mutex != null && this.FirstInstance)
                    {
                        this.mutex.WaitOne();
                        this.mutex.ReleaseMutex();
                        this.mutex = null;
                    }
                }

                this.disposed = true;
            }

            /// <summary>
            /// Fires the OtherInstanceCreated event.
            /// </summary>
            /// <param name="arguments">
            /// The arguments to pass with the <see cref="OtherInstanceCreatedEventArgs"/> class.
            /// </param>
            protected virtual void OnOtherInstanceCreated(OtherInstanceCreatedEventArgs arguments)
            {
                EventHandler<OtherInstanceCreatedEventArgs> handler = this.OtherInstanceCreated;

                if (handler != null)
                {
                    handler(this, arguments);
                }
            }

            /// <summary>
            /// Listens for arguments on a named pipe.
            /// </summary>
            private void Listen()
            {
                try
                {
                    using (var server = new NamedPipeServerStream(this.identifier.ToString()))
                    {
                        using (var reader = new StreamReader(server))
                        {
                            server.WaitForConnection();
                            var arguments = new List<string>();

                            while (server.IsConnected)
                            {
                                arguments.Add(reader.ReadLine());
                            }

                            this.synchronizationContext.Post(o => this.OnOtherInstanceCreated(new OtherInstanceCreatedEventArgs(arguments)), null);                        
                        }
                    }

                    // start listening again.
                    this.Listen();
                }
                catch (IOException)
                {
                    // Pipe was broken, listen again.
                    this.Listen();
                }          
            }

            /// <summary>
            /// Listens for arguments being passed from successive instances of the applicaiton.
            /// </summary>
            private void ListenAsync()
            {
                Task.Factory.StartNew(this.Listen, TaskCreationOptions.LongRunning);
            }

            /// <summary>
            /// Passes the given arguments to the first running instance of the application.
            /// </summary>
            /// <param name="arguments">
            /// The arguments to pass.
            /// </param>
            private void NotifyFirstInstance(IEnumerable<string> arguments)
            {
                try
                {
                    using (var client = new NamedPipeClientStream(this.identifier.ToString()))
                    {
                        using (var writer = new StreamWriter(client))
                        {
                            client.Connect(200);

                            if (arguments != null)
                            {
                                foreach (string argument in arguments)
                                {
                                    writer.WriteLine(argument);
                                }
                            }
                        }
                    }
                }
                catch (TimeoutException)
                {
                    // Couldn't connect to server
                }
                catch (IOException)
                {
                    // Pipe was broken
                }
            }



 #endregion
    }

/// <summary>
/// Holds a list of arguments given to an application at startup.
/// </summary>
public class OtherInstanceCreatedEventArgs : EventArgs
{
    #region Constructors and Destructors

    /// <summary>
    /// Initializes a new instance of the <see cref="OtherInstanceCreatedEventArgs"/> class.
    /// </summary>
    /// <param name="args">
    /// The command line arguments.
    /// </param>
    public OtherInstanceCreatedEventArgs(IEnumerable<string> args)
    {
        this.Args = args;
    }

    #endregion

    #region Public Properties

    /// <summary>
    /// Gets the startup arguments.
    /// </summary>
    public IEnumerable<string> Args { get; private set; }

    #endregion
}

然后在您的主类中,您可以创建类的实例,该实例将一直保留到应用程序运行为止。您可以检查其他实例是否由 FirstInstance 属性创建,并获得由 OtherInstanceCreated 事件创建的其他实例的通知。

关于c# - 在同一程序的多个实例之间同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10272302/

相关文章:

c# - 为字符串属性 ASP.NET MVC4 创建 EditorTemplate

c# - 关于可视化 Web GUI 的建议

ios - NSURLSession 线程 : Tracking multiple background downloads

mono - 如何在单声道下强制应用程序的单个实例?

c# - 在配置文件中使用引号作为值

c# - 在哪里可以找到 IIS 管理器连接字符串的位置

java - 将 Java Applet 限制为只有一个实例

java - 单个程序实例

.net - 在不影响事件的情况下检查 AutoResetEvent 的值

java - 无阻塞地原子读取 n 个字节