[英]Syncing between multiple instances of the same program
我手上有一个非常复杂的编程问题,所以请耐心等待几分钟。
我决定要在WPF(C#)中创建媒体播放器,但遇到了麻烦。
我希望我的应用程序是单个实例,这样当用户双击服务器文件时,该程序将只运行一次并将所有文件排队播放。
我尝试了几种方法来实现它,包括Microsoft的单实例实现,但似乎没有任何效果,直到我决定创建自己的 ,就像在某种东西上实现了它一样(这可能也在Internet上的某个地方,但是它没有出现)
基本上,我使用一个命名的互斥锁来防止打开多个实例,并强制其他实例将其参数写入文件,然后,创建互斥锁的实例将读取该文件。 不用说,就性能而言,这是非常非常无效的,但是无论如何,这是我对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块似乎什么也没做。 实际上,我认为这比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
事件创建的其他实例的通知。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.