進程及進程間通信


基礎知識

1.進程:具有獨立功能的程序在一個數據集合上一次動態的執行過程。通俗點講就是“一個正在運行的程序”

2.程序:靜態的程序以文件的形式保存在磁盤上。

3.操作系統的進程管理:

每一個正在運行的程序都對應着一個獨立的進程,當這些程序裝入內存開始執行時,操作系統會為每個進程創建好相關的數據結構。由於操作系統可以同時裝入多個程序,為此必須有一種方法來保證這些同時運行的程序不相互影響,不會由於一個程序出現異常而直接影響其他程序,甚至操作系統的正常運行。位於操作系統核心的“進程管理“模塊負責管理並行執行的多個程序。

4.操心系統的用戶模式和核心模式:

Windows設計了兩種代碼運行的環境——用戶模式和核心模式(用戶態與核心態)。普通的應用程序運行於用戶模式中,而操作系統的關鍵代碼(比如負責分配與回收內存,創建和銷毀進程等功能代碼)運行核心模式下。運行於核心模式下的代碼可以訪問系統內存和執行所有的CPU指令,用戶模式下運行的代碼則只擁有”有限的權限“。當用戶模式下的應用程序訪問系統核心數據時,它必須發出一個”系統調用“提出訪問核心數據的申請,操作系統內核接到申請,有運行在核心模式下的代碼去訪問這些數據,然后將結果再”轉發“給處於用戶模式下的代碼。Windows應用程序通過調用Win32API函數來實現從”用戶模式“到”核心模式“的轉換。

5.句柄:

操作系統核心會不斷地創建和銷毀"核心對象",為了便於跟蹤和訪問這些對象,此操作系統為這些對象分配了標識,這是一個32位的整數,被稱為"句柄(Handle)"

6.操作系統采用引用計數法來決定銷毀核心對象的時機:

當一個線程調用某個Win32API函數創建一個核心對象之后,此核心對象的初始引用計數為1,應用程序代碼中對其句柄的每次引用都會導致計數加1.線程用完以后應該關閉句柄,此時引用對象減1,當其值減為1時,操作系統銷毀這一核心對象,並回收其占用的各種資源。

7..NET對於"普通對象"與"核心對象"不加區分,使用new關鍵字就可以創建任何一種類型,而對象的銷毀工作由CLR負責。

8.線程

線程是CPU調度的基本單位,因為操作系統會給進程分配大量的資源,如果直接以進程作為調度CPU運行的基本單位,那么當進程投入運行和退出運行時,必然花費大量的計算資源在進程運行環境的切換上(保護現場等)。這會嚴重影響操作系統的性能。為此,操作系統將進程切分為多個“線程(thread)",雖然線程也需要有一個運行環境,但這個運行環境往往只涉及到當前一些寄存器的值,數據量小,切換速度快得多。

9. 進程是系統分配各種資源的單位,而線程則是操作系統分配CPU的基本單位

10.線程上下文

線程的運行環境被稱為“線程上下文”,包括為使線程在線程的所屬進程地址空間中繼續運行所需的所有信息(如線程的CPU寄存器,線程堆棧和線程局部存儲區)當從一個線程切換到另一個線程時,操作系統將保存被換出線程的線程上下文。

11.CLR如何管理進程與線程

.NET是一個托管的運行環境,在其上運行的進程稱為“托管進程”,而在托管進程中創建的線程自然就是“托管線程”。操作系統直接創建的進程和線程被稱為本地進程和本地線程

Process類代表托管進程,它實際上封裝的是本地進程。,每一個托管進程都對應着一個操作系統真實的本地進程。

Thread類代表托管線程,每一個托管線程對應着一個函數(線程函數),托管線程執行的過程就是線程函數執行的過程,線程函數的代碼執行完了,線程也就執行完了。

Thread類與操心系統的真實的本地線程不是一一對應的,它代表是一個“邏輯線程”,有CLR負責創建與管理。.NET Framework中另有一個ProcessThread用於表示操作系統真實的本地線程。

在操作系統中,線程直接運行於進程內部,而在.NET托管環境下,托管線程與托管進程之間還有一個中間層次——應用程序域

2.進程通信

進程通信:正在運行的進程相互交換信息。

每個進程都擁有自己的地址空間,其它進程不能直接訪問。通常通過一個第三方媒介間接地在進程之間交換信息。通常有下面幾種方式:

剪貼板,共享同一文件,COM,.NET4.0內存映射文件,WCF

①使用剪貼板在進程間傳送對象

 剪貼板是一個供應用程序使用的公共區域,在Windows上運行的所有程序在需要時都可以使用剪貼板存放的信息

namespace UseClipboard
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }
        //圖片
        private Image bmp
        {
            get
            {
                return pictureBox1.Image;
            }
            set
            {
                pictureBox1.Image = value;
            }
        }
        //圖片說明
        private string info
        {
            get
            {
                return txtImageInfo.Text;
            }
            set
            {
                txtImageInfo.Text = value;
            }
        }

       

        private void btnLoadPic_Click(object sender, EventArgs e)
        {
            ChooseImageFile();
        }

        //選擇圖片
        private void ChooseImageFile()
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                bmp = new Bitmap(openFileDialog1.FileName);
            }
        }
        //根據用戶設定的信息創建對象
        private MyPic CreateMyPicObj()
        {
            MyPic obj = new MyPic();
            obj.pic = bmp;
            obj.picInfo = info;
            return obj;
        }

        //將對象復制到剪貼板上
        private void CopyToClipboard()
        {
            //創建MyPic對象
            MyPic obj = CreateMyPicObj();
         
            //創建一個數據對象,將MyPic類型的對象裝入
            IDataObject dataobj = new DataObject(obj);
            //其它類型的數據也可以裝入到數據對象中
            dataobj.SetData(DataFormats.UnicodeText, info);
            dataobj.SetData(DataFormats.Bitmap, bmp);
            //復制到剪貼板上,第二個參數表明程序退出時不清空剪貼板
            Clipboard.SetDataObject(dataobj,true );
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            Close();
           
        }

        private void btnCopyToClipboard_Click(object sender, EventArgs e)
        {
            CopyToClipboard();
        }

        //從剪貼板獲取數據
        private void PasteFromClipboard()
        {
            //剪貼板上有我需要的數據嗎?格式為“項目名稱.數據格式名”
            if (Clipboard.ContainsData("UseClipboard.MyPic") == false)
                return;
            //讀取數據
            IDataObject clipobj = Clipboard.GetDataObject();
            //將數據轉換為需要的類型
            MyPic mypicobj = clipobj.GetData("UseClipboard.MyPic") as MyPic;
            //從數據對象中分解出需要的數據
            info = mypicobj.picInfo;
            pictureBox1.Image = mypicobj.pic;
        }

        private void btnPasteFromClipboard_Click(object sender, EventArgs e)
        {
            PasteFromClipboard();
        }

    
    }
}
View Code

剪貼板無法通知其它進程數據已經放到剪貼板上了。

②使用FileSystemWatcher實現進程同步

FileSystemWatcher是.NET提供組件,它可以監控特定的文件夾或文件,比如在此文件夾中的文件被刪除或內容被改變時引發對應的事件。通過使用FileSystemWatcher組件,讓多個進程同時監控一個文件,就可以讓文件充當“臨時的”進程間通信渠道。

③使用內存映射文件實現進程通信

原理:在內存中開辟出一塊存放數據的專用區域,這區域往往與硬盤上特定的文件相對應。進程將這塊內存區域映射到自己的地址空間中,訪問它就像訪問普通內存一樣。

MemoryMappedFile對象表示一個內存映射文件,通過它的CreateFromFile根據磁盤現有文件創建內存映射文件。

MemoryMappedFile對象創建之后,並不能直接對其進行讀寫,必須通過MemoryMappedViewAccessor來訪問創建的內存映射文件,MemoryMappedViewAccessor的Write<T>,Read<T>方法來執行讀寫操作。注意:T必須是值類型。之所以不能是引用類型,是因為引用類型保存在托管堆上面,計算機需要消耗性能來計算對象的大小。

如果需要保存引用類型(例如圖片),可以采用序列化的方式。MemoryMappedFile類可以創建一個MemoryMappedViewStream對象,通過它可以序列化對象。如下:

//創建或打開內存映射文件
MemoryMappedFile memoryFile=MemoryMappedFile.CreateOrOpen(...);
//創建內存映射流
MemoryMappedViewStream stream=memoryFile.CreateViewStream();
//創建要在進程之間交換的信息對象
MyObj=...
//向內存映射流中序列化對象
IFormatter formatter=new BinaryFormatter();
stream.Seek(0,SeekOrigin.Begin);
formatter.Serialize(stream,obj);

④使用WCF通過管道實現進程通信

管道(pipe):是Windows提供的一種進程間通信機制,用於在兩個進程間相互傳送數據。

Windows提供兩種管道:

匿名管道(Anonymous Pipe):單向通信,兩個通信的進程應該是父子關系,父進程在創建子進程時,負責將代表匿名管道的句柄傳給子進程,子進程獲得句柄后,即可接收從父進程發來的消息。

命名管道(Named Pipe):擁有在本機唯一的名字,可以用在一個服務端進程和多個客戶進程同事進行單向或雙向通信。命名管道支持基於消息的通信模式,這就是說,一個進程可以向另一方進程連續發送多個消息

讀書筆記《.NET4.0面向對象編程漫談》作者:金旭亮老師


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM