進程與進程間通訊

多線程開發掃盲系列第一編:進程與進程間通訊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();        }    }

相關文章
相關標籤/搜索