学习Mutex的心得,不一定对,先记录一下。
同步技术分为两大类,锁定和信号同步。
锁定分为:Lock、Monitor
信号同步分为:AutoResetEvent、ManualResetEvent、Semaphore以及Mutex。他们都继承自WaitHandle,
AutoResetEvent、ManualResetEvent在内存中维护一个布尔型变量,如果为false则阻塞,如果为true则解除阻塞
Semaphore在内存中维护一个整型变量,如果为0则阻塞,如果大于0则解除阻塞,每解除一个阻塞其值减一
AutoResetEvent、ManualResetEvent、Semaph提供单进程内的线程同步
Mutex提供跨应用程序域的线程阻塞和解除的能力,主要用于互斥访问。
下面是一个使用Mutex进行互斥访问的演示例子。
软件打开时,如果接收到输入则创建一个互斥锁,并持有锁,直到再次接收到输入,然后释放锁,如果再次输入又创建锁, 如此循环。
假设app1创建一个互斥锁,然后持有锁,并对共享资源进行操作,那么app2就不能再次创建互斥锁,据此就能判断共享资源释放被别的进程占用。
如果app1使用完了共享资源,释放了互斥锁,则app2就可以创建互斥锁,据此可以判断共享资源可以被访问了。
以下是app1代码
1、Mutex用于进程间的同步
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MutexApplication { class Program { static void Main(string[] args) { new App1Class().write(); } } class App1Class { public void write() { while (true) { Console.WriteLine("please input a word to create mutex"); Console.WriteLine(""); Console.ReadLine(); //创建一个互斥锁,如果创建成功,则isCreate返回true if (create()) { Console.WriteLine("Application1 get the mutex 'test'"); Console.WriteLine(""); Console.WriteLine("please input a word to dispose mutex"); Console.WriteLine(""); //随便输入一个word,释放互斥锁 Console.ReadLine(); dispose(); Console.WriteLine("Application1 dispose mutex 'test'"); Console.WriteLine(""); } } } System.Threading.Mutex mutext = null; private bool create() { bool isCreate = false;
//如果进程中没有名字为test的mutex,则创建成功isCreate为true,第一个参数如果为true,则指定创建mutex的线程拥有此mutex。 mutext = new System.Threading.Mutex(true, "test", out isCreate); if (!isCreate) { mutext.Dispose(); mutext = null; GC.Collect(); GC.WaitForPendingFinalizers(); } return isCreate; } private void dispose() { mutext.ReleaseMutex(); mutext.Dispose(); mutext = null; GC.Collect(); GC.WaitForPendingFinalizers(); } } }
2、app2 代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { new App2Class().write(); } } class App2Class { public void write() { while (true) { Console.WriteLine("please input a word to create mutex"); Console.WriteLine(""); Console.ReadLine(); //创建一个互斥锁,如果创建成功,则isCreate返回true if (create()) { Console.WriteLine("Application2 get the mutex 'test'"); Console.WriteLine(""); Console.WriteLine("please input a word to dispose mutex"); Console.WriteLine(""); //随便输入一个word,释放互斥锁 Console.ReadLine(); dispose(); Console.WriteLine("Application2 dispose mutex 'test'"); Console.WriteLine(""); } } } System.Threading.Mutex mutext = null; private bool create() { bool isCreate = false; mutext = new System.Threading.Mutex(true, "test", out isCreate); if (!isCreate) { mutext.Dispose(); mutext = null; GC.Collect(); GC.WaitForPendingFinalizers(); } return isCreate; } private void dispose() { mutext.ReleaseMutex(); mutext.Dispose(); mutext = null; GC.Collect(); GC.WaitForPendingFinalizers(); } } }
结果图:
3、但是在实际应用中多用于单例模式,用于判断应用程序是否被创建。
//单例模式 bool bCreatedNew; System.Threading.Mutex mutex = new System.Threading.Mutex(false, Application.ProductName, out bCreatedNew); if (!bCreatedNew) { //如果已经创建,则获取应用程序的句柄,并显示出来,或者提示已经运行 IntPtr hwnd = SingleProcess.FindWindow(null,Global.fromTitle); SingleProcess.ShowWin(hwnd); //SingleProcess.Singling("消息中心服务器"); //MessageBox.Show("打开失败,已有消息中心服务正在运行!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; namespace WcfAlarmCenter { public class SingleProcess { //根据主窗体句柄显示窗体 public static void ShowWin(IntPtr hwnd) { ShowWindow(hwnd, SW_RESTORE); SwitchToThisWindow(hwnd, true); Rect windowRec; GetWindowRect(hwnd, out windowRec); System.Drawing.Rectangle rect = System.Windows.Forms.SystemInformation.VirtualScreen; SetWindowPos(hwnd, HWND_TOP, (rect.Width - (windowRec.Right - windowRec.Left)) / 2, (rect.Height - (windowRec.Bottom - windowRec.Top)) / 2, 0, 0, SWP_NOSIZE); } private static string _formText;// = string.Empty; private static Process _process = null; /// <summary> /// /// </summary> /// <param name="str"></param> public static void Singling(string formtext) { _formText = formtext; Process instance = GetInstance(); if (instance != null) //首先确定有无进程 { _process = instance; if (_process.MainWindowHandle.ToInt32() != 0) //是否托盘化 { //HandleRunningInstance(pro); ShowWin(_process.MainWindowHandle); } else { CallBack myCallBack = new CallBack(Report); EnumWindows(myCallBack, 0); } //System.Environment.Exit(System.Environment.ExitCode); } } public static Process GetInstance() { Process current = Process.GetCurrentProcess(); Process[] processes = Process.GetProcessesByName(Application.ProductName);//current.ProcessName); //遍历正在有相同名字运行的例程 foreach (Process process in processes) { //忽略现有的例程 if (process.Id != current.Id) { //if (process.MainModule.FileName == current.MainModule.FileName) { //返回另一个例程实例 return process; } } } //没有其它的例程,返回Null return null; } private static bool Report(IntPtr hwnd, int lParam) { //获得窗体标题 StringBuilder sb = new StringBuilder(100); GetWindowText(hwnd, sb, sb.Capacity); int calcID; //获取进程ID GetWindowThreadProcessId(hwnd, out calcID); if ((sb.ToString() == _formText) && (_process != null) && (calcID == _process.Id)) //标题栏、进程id符合 //if (pro != null && calcID == pro.Id) //进程id符合 { ShowWin(hwnd); return true; } return true; } #region win32 API /// <summary> /// 获取窗体句柄 /// </summary>,两个参数至少要知道一个 /// <param name="lpClassName">窗体类名,可以通过Spy++获取,为null表示忽略</param> /// <param name="lpWindowName">窗体标题,Text属性,为null时表示忽略</param> /// <returns></returns> [DllImport("user32.dll", EntryPoint = "FindWindow")] public extern static IntPtr FindWindow(string lpClassName, string lpWindowName); /// <summary> /// 根据窗体句柄获得窗体标题 /// </summary> /// <param name="hWnd"></param> /// <param name="lpText"></param> /// <param name="nCount"></param> /// <returns></returns> [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpText, int nCount); /// <summary> /// 枚举窗体 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> [DllImport("user32")] private static extern int EnumWindows(CallBack x, int y); private delegate bool CallBack(IntPtr hwnd, int lParam); /// <summary> /// 根据窗体句柄获得其进程ID /// </summary> /// <param name="hwnd"></param> /// <param name="ID"></param> /// <returns></returns> [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID); /// <summary> /// 修改位置、大小 /// </summary> /// <param name="hWnd"></param> /// <param name="hWndInsertAfter"></param> /// <param name="X"></param> /// <param name="Y"></param> /// <param name="cx"></param> /// <param name="cy"></param> /// <param name="uFlags"></param> /// <returns></returns> [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); /// <summary> /// Retains the current size (ignores the cx and cy parameters). /// </summary> static uint SWP_NOSIZE = 0x0001; static int HWND_TOP = 0; public struct Rect { public int Left; public int Top; public int Right; public int Bottom; } [DllImport("user32.dll")] private static extern int GetWindowRect(IntPtr hwnd, out Rect lpRect); /// <summary> /// 显示窗体,同 ShowWindowAsync 差不多 /// </summary> /// <param name="hwnd"></param> /// <param name="nCmdShow"></param> /// <returns></returns> [DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)] private static extern int ShowWindow(IntPtr hwnd, int nCmdShow); private const int SW_RESTORE = 9; /// <summary> /// 该函数设置由不同线程产生的窗口的显示状态。 (没用) /// </summary> /// <param name="hWnd">窗口句柄</param> /// <param name="cmdShow">指定窗口如何显示。查看允许值列表,请查阅ShowWlndow函数的说明部分。</param> /// <returns>如果函数原来可见,返回值为非零;如果函数原来被隐藏,返回值为零。</returns> [DllImport("User32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow); /// <summary> /// 该函数将创建指定窗口的线程设置到前台,并且激活该窗口。 /// 键盘输入转向该窗口,并为用户改各种可视的记号。系统给创建前台窗口的线程分配的权限稍高于其他线程。 /// (没用) /// </summary> /// <param name="hWnd">将被激活并被调入前台的窗口句柄。</param> /// <returns>如果窗口设入了前台,返回值为非零;如果窗口未被设入前台,返回值为零。</returns> [DllImport("User32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); private const int WS_SHOWNORMAL = 1; /// <summary> /// 窗体焦点 /// </summary> /// <param name="hWnd"></param> /// <param name="fAltTab"></param> [DllImport("user32.dll ", SetLastError = true)] private static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab); #endregion } }
。
参考
http://www.cnblogs.com/city22/archive/2007/02/02/638260.html
C#多线程之二:ManualResetEvent和AutoResetEvent
。