进程与进程间通讯

多线程开发扫盲系列第一编:进程与进程间通讯windows

 

1. 操做系统的进程与线程管理    多线程

2. 进程的启动和终止    app

3. 进程通讯    函数

 3.1 经过剪贴版进程交换信息    this

 3.2 FileSystemWatch实现进程同步    spa

 3.3 使用内存映射文件实现进程通讯    操作系统

 3.4 进程间的通知机制    线程

 

1.进程与线程管理

进程(process)是一个具备独立功能的程序在一个数据集合上的一次动态执行过程。这个定义太理论化了,用一句通俗的话取代它:进程能够简单理解为一个正在运行的程序。
程序与进程的区别能够用图形像地表达出来。设计


    Window设计了两种代码运行环境,用户模式(User Mode)和核心模式(kernel Mode),普通的应用程序运行于用户模式中,而操做系统的关键代码(好比负责分配与回收内存、建立和销毁进程等功能的代码)运行于核心模式下。 在windows中,”系统调用”主要指win32API中的特定函数,因此,windows应用程序经过调用win32API函数来实现从”用户模式”到”核心模式”的转换
    code

    句柄与系统核心对像
    位于操做系统内核中,仅容许运行于”核心模式”下的代码访问的数据被称为”核心对像”,操做系统在运行时,会在系统核心不断地建立和销毁”核心对像”,为了便于跟踪和访问这些对像,操做系统为这些对像分配了标识,这是一个32位的整数,被称为”句柄”。许多win32 API函数经过句柄来定位所要访问的系统核心对像。在.NET托管环境中,.NET应用程序对”普通对像”和”核心对像”不加区分,使用New关键字就能够建立任何一种类型的对像,而对像的销毁工做邮CLR负责。
   

    Windows操做系统使用线程做为CPU调度的基本单位,一个进程能够划分多个线程,也能够只有一个线程。它拥有一个线程标识(ThreadID),一组CPU寄存器,两个堆栈和一个专有的线程局部存储区(Thread Local Storage,TLS)。属于同一个进程的线程共享进程所拥有的资源。
进程是系统分配各类资源(好比内存)的单位,而线程则是操做系统分配CPU(即处理机调度)的基本单位。

 

2.进程的启动与终止

    .NET应用程序控制进程的核心是Process类,Process类继承自Component类,一般又称为Process组件。Process组件表明一个托管进程,底层封装的是操做系统的本地进程。另外一个重要的类是ProcessStartInfo类,这个类封装了进程启动时的各类控制参数。

以下继承结构图


使用Process.Start方法启动进程
Process.Start(“IExplore.exe”)
Process.Start(“IExplore.exe”,”www.baidu.com”)
有时候咱们但愿向进程传送一些控制信息,好比此进程打开一个网页时最小化,能够这么来作
ProcessStartInfo info = new ProcessStartInfo("IExplore.exe");
info.WindowStyle=ProcessWindowStyle.Minimized;  //自动最小化
info.Arguments="www.sina.cn";  //自动访问新浪网
Process.Start(info);  //启动进程   


经过调用CloseMainWindow方法发出的结束进程运行的请求不会强制应用程序当即退出,它至关于用户直接点击主窗口上的关闭按钮。应用程序能够在退出前请求用户确认,也能够拒绝退出。

Kill方法强制关闭一个进程,与CloseMainWindow方法不一样,Kill方法其实是请求操做系统直接结束进程,它不给要关闭的进程保存数据的机会,所以除非要保存的进程没有任何数据需保存,不然不要采用Kill方法直接结束某个进程。

 

3.进程通讯

3.1 经过剪贴版进程交换信息

所谓进程通讯,是指正在运行的进程之间相互交换信息。
每一个进程都拥有本身的地址空间,其余进程不能直接访问,所以一般须要经过一个第三方媒介间接地在进程之间交换信息。
剪贴板是最经常使用的进程间交换信息的媒介之一。

剪贴版至关于一个"物品临时寄存器",一次只能保存一个"物品",并且这个"物品"是你们共享的,好比使用work复制了一段文本在剪贴板上,如今又使用"画图"程序将一幅图放在剪贴板上,则图片数据将替换掉文本数据。再好比使用画图程序将一幅画放在剪贴板上,则work,写字板,photoshop等其它应用程序均可以从剪贴板中获取这些数据。

剪贴板中能够保存多种类型数据,.NET定义了一个DataFormats类,定义了剪贴板中能够存放的数据类型,以下图

字段名称

说明

Bitmap

Windows位图格式

Dib

Windows与设备无关的位图(DIB)格式

EnhancedMetafile

Windows加强型图元文件格式

Html

由Html数据组成的文本

MetafilePict

Windows图元文件格式,Windows客体不直接使用此格式

OemText

标准Windows原始设备制造商(OEM)文本格式

Palette

Windows调色板格式

Rtf

由Rich Text Format(RTF)数据组成的文本

Serializable

可序列化的对像

StringFormat

Windows窗体字符串类格式,Windows窗体使用此格式存储字符串对像

Text

标准ANSI文本格式

UnicodeText

标准Windows Unicode文本格式

 以下示例,复制几个文件或图片,点击剪贴板上有什么按钮,看结果

实现代码:

        private void btnShowBoard_Click(object sender, EventArgs e)
        {
            IDataObject data = Clipboard.GetDataObject();  //获取剪贴板上的数据
            richTextBox1.Text = "";
            if (data == null)
                return;
            string[] str = data.GetFormats();   //获取剪贴板上数据类型
            foreach (string s in str)
            {
                richTextBox1.AppendText(s+"\n");
            }
        }

 

 

以下示例,即基于剪贴板交换数据,打开两个此程序,程序A点装入图片-复制到剪贴板,程序B点从剪贴板粘贴。便可看到数据在两个进程间交换

核心代码:

    //装入图片
    private void btnLoadImage_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                bmp = new Bitmap(openFileDialog1.FileName);
            }
        }

     //保存到剪贴板       
      private void btnCopyToBoard_Click(object sender, EventArgs e)
        {
            MyPic mypic = new MyPic { image = bmp, info = info };
            IDataObject dataobj = new DataObject(mypic);
            dataobj.SetData(DataFormats.UnicodeText, info);
            dataobj.SetData(DataFormats.Bitmap, bmp);
            Clipboard.SetDataObject(dataobj, true);   
        }

    //从剪贴板粘贴
        private void btnPasteFromBoard_Click(object sender, EventArgs e)
        {
            if (Clipboard.ContainsData("UseClipboard.MyPic") == false)
                return;

            IDataObject clipobj = Clipboard.GetDataObject();
            //将数据转换为须要的类型
            MyPic mypicobj = clipobj.GetData("UseClipboard.MyPic") as MyPic;
            //从数据对象中分解出须要的数据
            info = mypicobj.info;
            pictureBox1.Image = mypicobj.image;
        }

剪贴板用起来很是方便,但它有个缺点,它无法通知其余进程数据已放到剪贴板上了,除非在等待接收数据的进程中设计一个辅助线程定时监控剪贴板,在数据来时主动从剪贴板中获取数据,但这并非最佳方式。

3.2 FileSystemWatch实现进程同步

FileSystemWatcher是.Net Framework所提供的一个组件,它能够监控特定的文件夹或文件,好比在此文件夹中某文件被删除或内容被改变时引起对应的事件。以下所示两个程序一个用于读,一个用于写,当在frmwrite修改了文件点保存时,frmreader会同步显示文件更新后的内容

文件写入的方法
 using (StreamWriter sw = new StreamWriter(new FileStream(FileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read), Encoding.Default))
   {
       sw.Write(richTextBox1.Text);
   }

文件监控的代码

namespace FileReader
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        string FileName;

       //载入文件
        public void LoadFile()
        {
            try
            {
                using (StreamReader sr = new StreamReader(new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Encoding.Default))
                {
                    richTextBox1.Text = sr.ReadToEnd();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        public void SetupFileSystemWatch()
        {
            fileSystemWatcher1.Filter = Path.GetFileName(FileName);   //监控的文件
            fileSystemWatcher1.Path = Path.GetDirectoryName(FileName);  //监控的文件路径
            fileSystemWatcher1.NotifyFilter = NotifyFilters.Size;   //当文件大小改变时,触发事件
        }

        private void btnCheck_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                FileName = openFileDialog1.FileName;
                LoadFile();
                SetupFileSystemWatch();
            }
        }

        private void fileSystemWatcher1_Changed(object sender, FileSystemEventArgs e)
        {
            LoadFile();
        }

    }
}

FileSystemWatcher组件的经常使用事件

Changed

当更改指定文件夹中的文件和目录时发生

Created

当在指定文件夹中建立文件和目录时发生

Deleted

删除指定文件夹或目录时发生

Renamed

重命名指定文件夹中 或目录时发生

3.3 使用内存映射文件实现进程通讯

所谓内存映射就是在内存中开辟出一块存放数据的专用区域,这区域每每与硬盘上特定的文件相对应。进程将这块内存区域映射到本身的地址空间中,访问它就像是访问普通的内存同样,.NET中使用MemoryappedFile对像表示一个内存映射文件,经过它的CreateFromFile方法根据磁盘现有文件建立内存映射文件,MemoryMappedFile对像建立以后,不能直接对其读写,还须经过一个MemoryMappedViewAccessor对像来访问。

以下示例:输入两个数,保存到内存取映射文件,而后再打开一个程序点击提取便可把内存映射中的数据提取出来

代码以下:

    private int FileSize = 1024 * 1024;  //设为映射文件大小
        private MemoryMappedFile file = null;  
        private MemoryMappedViewAccessor accor = null;
        private void Init()
        {
            file = MemoryMappedFile.CreateOrOpen("UseMMFBetweenProcess", FileSize);  //建立内存取映射文件
            accor = file.CreateViewAccessor();  //建立映射文件视图
            toolStripStatusLabel1.Text = "内存文件映射或建立成功";
        }
        private MyStructure data;
        private void btnSave_Click(object sender, EventArgs e)
        {
            data.IntValue = Convert.ToInt32(txtInt.Text);
            data.FloatValue =float.Parse(txtFloat.Text);
            accor.Write<MyStructure>(0,ref data);   //将结构对像保存到映射文件中
            toolStripStatusLabel1.Text = "数据已保存到内文件中";

        }

        private void btnGet_Click(object sender, EventArgs e)
        {
            accor.Read<MyStructure>(0, out data);  //从映射文件中取出结构对像
            txtInt.Text = data.IntValue.ToString(); ;
            txtFloat.Text = data.FloatValue.ToString();
            toolStripStatusLabel1.Text = "成功从内存中提取了数据";
        }

3.4 进程间的通知机制

 进程之间的数据传送有多种方式,但大多数进程通讯手段都缺少一种通知机制,本节介绍一种比较简便的.NET线程同步对像Mutext和EventWaitHandle实现进程通知机制的方法

示例以下:点击发送端程序中的的click me,接收端窗体会记录点击次数

 

 //发送端代码

 public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private EventWaitHandle handle;
        private const string ProcessSynchronizeEventName = "ProcessSynchronizeEvent";
        private void button1_Click(object sender, EventArgs e)
        {
            handle.Set();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                handle = EventWaitHandle.OpenExisting(ProcessSynchronizeEventName);
                if (handle != null)
                {
                    MessageBox.Show("只能运行一个实例");
                    handle = null;
                    Close();
                }
            }
            catch (WaitHandleCannotBeOpenedException)
            {
                handle = new EventWaitHandle(false, EventResetMode.ManualReset, ProcessSynchronizeEventName);
                labInfo.Text = "eventhandle已建立";
            }

        }
    }

 

//接收端代码

 public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private EventWaitHandle hEvent = null;
        private const string MyProcess = "ProcessSynchronizeEventResponsor";
        private const string ProcessSynchronizeEventName = "ProcessSynchronizeEvent";

        private void GetEventHandle()
        {
            try
            {
                hEvent = EventWaitHandle.OpenExisting(ProcessSynchronizeEventName);
                if (hEvent != null)
                {
                    Thread th = new Thread(WaitForHandle);
                    th.IsBackground = true;
                    th.Start();
                  
                }
            }
            catch (WaitHandleCannotBeOpenedException)
            {
                MessageBox.Show("请先运行程序ProcessSynchronizeEventSource的一个实例");
                Close();
            }
        }
        private int count;
        void WaitForHandle()
        {
            while (true)
            {
                hEvent.WaitOne();
                count++;
                string info="服务端进程点击了" + count+"次";
                Action<string> showinfo = delegate(string a)
                {
                    labInfo.Text = a;
                };
                this.Invoke(showinfo, info);
                hEvent.Reset();
            }
        }

        private void Form1_Load(object sender, EventArgs e)        {            try            {                Mutex m = Mutex.OpenExisting(MyProcess);                if (m != null)                {                    MessageBox.Show("已有一个实例在运行");                    Close();                }            }            catch (WaitHandleCannotBeOpenedException)            {                Mutex m = new Mutex(false, MyProcess);            }            GetEventHandle();        }    }

相关文章
相关标签/搜索