多线程进程间通讯共享内存(Shared Memory with IPC with threads)

绪论

本文关注使用共享内存在多线程和进程之间共享内存的设计和通信。我将把本文分成两个部分:

  • 共享内存
  • 编码

关于共享内存

当一个程序加载进内存后,它就被分成叫作页的块。通信将存在内存的两个页之间或者两个独立的进程之间。总之,当一个程序想和另外一个程序通信的时候,那内存将会为这两个程序生成一块公共的内存区域。这块被两个进程分享的内存区域叫做共享内存。如果没有共享内存的概念,那一个进程不能存取另外一个进程的内存部分,因而导致共享数据或者通信失效。其次,为了简化共享数据的完整性和避免同时存取数据,内核提供了一种专门存取共享内存资源的机制。这称为互斥体或者mutex对象。

当一个进程想和另外一个进程通信的时候,它将按以下顺序运行:

  1. 获取mutex对象,锁定共享区域。
  2. 将要通信的数据写入共享区域。
  3. 释放mutex对象。

当一个进程从从这个区域读数据时候,它将重复同样的步骤,只是将第二步变成读取。

关于代码

为了程序能通信,共享内存区域应当在程序启动时候建立,至少这个例子是这样做的。在OnCreate函数中映射WM_CREATE消息通过使用以下代码实现(你可以通过ClassWizard WM_CREATE添加一个句柄来实现)。在做这些之前,写下如下的全局变量(在任何一个类外):

所有的事情都可以在实现共享内存文件的头文件中定义。它可以是一个对话框或者一个文档接口。

HANDLE kSendCommand; // Handle for "sending command" event

HANDLE kReceiveCommand; // Handle for "receiving command" event

 

HANDLE kSendMessage; // Handle for "sending message" event

HANDLE kReceiveMessage; // Handle for "receiving message" event

 

HANDLE kChildAck; // Handle for "acknowledgement from the child" event

 

CWinThread* thread; // The thread object

 

共享内存结构运行如下:

 

#define KILL_APP WM_USER+10 // Command to stop the process

#define RECV_MESSAGE WM_USER+20 // Command to receive the message

#define CHILD_START WM_USER+30 // Command when child starts

 

struct KSharedMemory

{

    DWORD processID; // ID of the process

    BOOL childAck; // Acknowledgment area

    char data[1000]; // The data

    UINT dataSize; // Size of the data

 

};

 

UINT StartProbing(LPVOID lParam); // The thread

 

 

The OnCreate function would look like this.

     CString title;

//    UpdateData(true);

    if (CDialog::OnCreate(lpCreateStruct) == -1)

        return -1;

   

   

    kProcessId = ::GetCurrentProcessId();

    title.Format("Process: %d",kProcessId);

    this->SetWindowText(title);

   

    /* Create a file map for sharing the memory. This is for sharing

                               the memory between same processes. */

    kMap = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,

                           0,sizeof(KSharedMemory),"KBuildDevelop");

    if(GetLastError() == ERROR_ALREADY_EXISTS)

    {

        // COMMENTED SECTION BELOW [TEST]: This was great.

        // Identifies if the process has been created earlier or not.

        /*MessageBox("One at a time please !!","One At A Time", MB_OK);

        ::PostQuitMessage(0);*/

        kMap = ::OpenFileMapping(FILE_MAP_WRITE,FALSE,"KBuildDevelop");

        kMutex = ::CreateMutex(NULL,FALSE,"KBuildDevelop");

        kParentOrChild = FALSE;

       

    }

    else

    {

        kParentOrChild = TRUE;

    }

 

    kShMem = (KSharedMemory*)::MapViewOfFile(kMap,FILE_MAP_WRITE,

                                       0,0,sizeof(KSharedMemory));

函数CreateFileMapping以文件映像的形式实现共享内存。函数MapViewOfFile使得共享区域能创建。

kSendCommand = ::CreateEvent(NULL,FALSE,FALSE,"SendCommand");

kSendMessage = ::CreateEvent(NULL,FALSE,FALSE,"SendMessage");

kReceiveMessage = ::CreateEvent(NULL,FALSE,FALSE,"ReceiveMessage");

kReceiveCommand = ::CreateEvent(NULL,FALSE,FALSE,"ReceiveCommand");

kChildAck = ::CreateEvent(NULL,FALSE,FALSE,"ChildAcknowledge");

函数CreateEvent为所有状态的创建事件,比如发送命令、接收命令、发送消息、接收消息和确认消息。

为了使消息能够映射用户定义的命令,在消息映射区域包含以下几行代码。

ON_MESSAGE(KILL_APP,OnKillApp)

ON_MESSAGE(RECV_MESSAGE,OnRecvMessage)

ON_MESSAGE(CHILD_START,OnChildStart)

在函数InitDialog中添加以下代码:

    if(kParentOrChild) // This is the parent. Receive the message from child.

    {

        this->SetWindowText("Parent: Receiving Command");

        GetDlgItem(IDC_BUTTON_KILL)->DestroyWindow();

        GetDlgItem(IDC_EDIT_MESSAGE)->EnableWindow(false);

        GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(false);

        thread = AfxBeginThread(StartProbing,GetSafeHwnd(),

                 THREAD_PRIORITY_NORMAL);

                 // Start Checking for Commands from Child

        if(thread != NULL)

        {

            UpdateData(true);

            m_status = "Parent waiting for messages ...";

            UpdateData(false);

        }

        else

        {

            UpdateData(true);

            m_status = "Thread not started ...";

            UpdateData(false);

        }

    }

    else

    {

        GetDlgItem(IDC_BUTTON_KILL)->EnableWindow(true);

        kShMem->childAck = TRUE;

        ::SetEvent(kChildAck);

        this->SetWindowText("Child: Send Command / Message to Parent");

    }

其他比较重要的函数如下:

// The thread process

UINT StartProbing(LPVOID lParam)

{

    while(1)

    {

        if(::WaitForSingleObject(kChildAck,10)== WAIT_OBJECT_0)

        /* Wait for acknowledgement from the child */

            PostMessage((HWND) lParam,CHILD_START,0,0);

        if(::WaitForSingleObject(kSendCommand, 10) == WAIT_OBJECT_0)

        {

            PostMessage((HWND) lParam, KILL_APP,0,0);

            ::SetEvent(kReceiveCommand);

            break;

        }   

        else

        {

            // Add code here to wait for another event.

            if(::WaitForSingleObject(kSendMessage, 10) == WAIT_OBJECT_0)

            {

                PostMessage((HWND) lParam, RECV_MESSAGE,0,0);

                ::SetEvent(kReceiveMessage);

            }

        }

   

    }

    return 0;

}

 

// When application is killed

void COneAtaTimeDlg::OnKillApp()

{

    PostQuitMessage(0);

}

 

//When kill button is pressed

void COneAtaTimeDlg::OnButtonKill()

{

    ::SetEvent(kSendCommand);

    ::ReleaseMutex(kMutex); // Release the mutex object

}

// When send button is pressed

void COneAtaTimeDlg::OnButtonSend()

{

    UpdateData(true);

    char buffer[100];

    sprintf(buffer,"%s",m_message);

    strcpy(kShMem->data,buffer);

    m_message=_T("");

    UpdateData(false);

    ::SetEvent(kSendMessage);// Set send message event

}

 

// When message is received

void COneAtaTimeDlg::OnRecvMessage()

{

    UpdateData(true);

    if(strcmp(kShMem->data,"bye")==0)

        PostQuitMessage(0);

    m_recvlist.AddString(kShMem->data);

    UpdateData(false);

}

 

// When child is started

void COneAtaTimeDlg::OnChildStart()

{

    UpdateData(true);

    m_status = "Child Started...";

    UpdateData(false);

 

}

Top of Form

 

 

 

 

 

 

 

Introduction

This article concentrates in shared memory design and communication between threads/programs using shared memory. I would break up this article into two sections:

About Shared Memory

When a program loads into the memory, it is broken up into pieces called pages. The communication would exist between the pages of memory or between two independent processes. Anyhow, when a program would like to communicate with another, there should be a common area in the memory for both the programs. This area which is shared between processes is called the Shared Memory. If there was no concept of shared memory, the section of memory occupied by a program could not be accessed by another one thus disabling the concept of sharing data or communication. Then again, in order to reduce integrity of shared data and to avoid concurrent access to the data, kernel provides a mechanism to exclusively access the shared memory resource. This is called mutual exclusion or mutex object.

When a process wants to communicate to another, the following steps take place sequentially:

  1. Take the mutex object, locking the shared area.
  2. Write the data to be communicated into the shared area.
  3. Release the mutex object.

When a process reads from the area, it should repeat the same steps, except that the step 2 should be Read.

About the Code

In order for the program to communicate, the shared memory region should be made when the program starts, at least in this case. This is done by using the following code in the OnCreate function mapped for WM_CREATE message. (You can do this explicitly by adding a handler for WM_CREATE by ClassWizard). Before doing that, write down the global variables (outside any class) as follows:

All these things can be defined in the header file for the implementation file of the shared memory. It can be a dialog box or a document interface.

Collapse Copy Code

HANDLE kSendCommand; // Handle for "sending command" event

HANDLE kReceiveCommand; // Handle for "receiving command" event

 

HANDLE kSendMessage; // Handle for "sending message" event

HANDLE kReceiveMessage; // Handle for "receiving message" event

 

HANDLE kChildAck; // Handle for "acknowledgement from the child" event

 

CWinThread* thread; // The thread object

 

The shared memory structure runs as below:

 

#define KILL_APP WM_USER+10 // Command to stop the process

#define RECV_MESSAGE WM_USER+20 // Command to receive the message

#define CHILD_START WM_USER+30 // Command when child starts

 

struct KSharedMemory

{

    DWORD processID; // ID of the process

    BOOL childAck; // Acknowledgment area

    char data[1000]; // The data

    UINT dataSize; // Size of the data

 

};

 

UINT StartProbing(LPVOID lParam); // The thread

 

 

The OnCreate function would look like this.

     CString title;

//    UpdateData(true);

    if (CDialog::OnCreate(lpCreateStruct) == -1)

        return -1;

   

   

    kProcessId = ::GetCurrentProcessId();

    title.Format("Process: %d",kProcessId);

    this->SetWindowText(title);

   

    /* Create a file map for sharing the memory. This is for sharing

                               the memory between same processes. */

    kMap = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,

                           0,sizeof(KSharedMemory),"KBuildDevelop");

    if(GetLastError() == ERROR_ALREADY_EXISTS)

    {

        // COMMENTED SECTION BELOW [TEST]: This was great.

        // Identifies if the process has been created earlier or not.

        /*MessageBox("One at a time please !!","One At A Time", MB_OK);

        ::PostQuitMessage(0);*/

        kMap = ::OpenFileMapping(FILE_MAP_WRITE,FALSE,"KBuildDevelop");

        kMutex = ::CreateMutex(NULL,FALSE,"KBuildDevelop");

        kParentOrChild = FALSE;

       

    }

    else

    {

        kParentOrChild = TRUE;

    }

 

    kShMem = (KSharedMemory*)::MapViewOfFile(kMap,FILE_MAP_WRITE,

                                       0,0,sizeof(KSharedMemory));

The CreateFileMapping function makes the shared memory as a file map. MapViewOfFile function enables sharing of the area created.

Collapse Copy Code

kSendCommand = ::CreateEvent(NULL,FALSE,FALSE,"SendCommand");

kSendMessage = ::CreateEvent(NULL,FALSE,FALSE,"SendMessage");

kReceiveMessage = ::CreateEvent(NULL,FALSE,FALSE,"ReceiveMessage");

kReceiveCommand = ::CreateEvent(NULL,FALSE,FALSE,"ReceiveCommand");

kChildAck = ::CreateEvent(NULL,FALSE,FALSE,"ChildAcknowledge");

The CreateEvent function creates events for all the states like sending command, receiving command, sending message, receiving message, and acknowledgement from the child.

To enable message mapping for the user defined commands, include the following lines in the message mapping area:

Collapse Copy Code

ON_MESSAGE(KILL_APP,OnKillApp)

ON_MESSAGE(RECV_MESSAGE,OnRecvMessage)

ON_MESSAGE(CHILD_START,OnChildStart)

In the InitDialog function, add the following lines:

Collapse Copy Code

    if(kParentOrChild) // This is the parent. Receive the message from child.

    {

        this->SetWindowText("Parent: Receiving Command");

        GetDlgItem(IDC_BUTTON_KILL)->DestroyWindow();

        GetDlgItem(IDC_EDIT_MESSAGE)->EnableWindow(false);

        GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(false);

        thread = AfxBeginThread(StartProbing,GetSafeHwnd(),

                 THREAD_PRIORITY_NORMAL);

                 // Start Checking for Commands from Child

        if(thread != NULL)

        {

            UpdateData(true);

            m_status = "Parent waiting for messages ...";

            UpdateData(false);

        }

        else

        {

            UpdateData(true);

            m_status = "Thread not started ...";

            UpdateData(false);

        }

    }

    else

    {

        GetDlgItem(IDC_BUTTON_KILL)->EnableWindow(true);

        kShMem->childAck = TRUE;

        ::SetEvent(kChildAck);

        this->SetWindowText("Child: Send Command / Message to Parent");

    }

The other important functions are:

Collapse Copy Code

// The thread process

UINT StartProbing(LPVOID lParam)

{

    while(1)

    {

        if(::WaitForSingleObject(kChildAck,10)== WAIT_OBJECT_0)

        /* Wait for acknowledgement from the child */

            PostMessage((HWND) lParam,CHILD_START,0,0);

        if(::WaitForSingleObject(kSendCommand, 10) == WAIT_OBJECT_0)

        {

            PostMessage((HWND) lParam, KILL_APP,0,0);

            ::SetEvent(kReceiveCommand);

            break;

        }   

        else

        {

            // Add code here to wait for another event.

            if(::WaitForSingleObject(kSendMessage, 10) == WAIT_OBJECT_0)

            {

                PostMessage((HWND) lParam, RECV_MESSAGE,0,0);

                ::SetEvent(kReceiveMessage);

            }

        }

   

    }

    return 0;

}

 

// When application is killed

void COneAtaTimeDlg::OnKillApp()

{

    PostQuitMessage(0);

}

 

//When kill button is pressed

void COneAtaTimeDlg::OnButtonKill()

{

    ::SetEvent(kSendCommand);

    ::ReleaseMutex(kMutex); // Release the mutex object

}

// When send button is pressed

void COneAtaTimeDlg::OnButtonSend()

{

    UpdateData(true);

    char buffer[100];

    sprintf(buffer,"%s",m_message);

    strcpy(kShMem->data,buffer);

    m_message=_T("");

    UpdateData(false);

    ::SetEvent(kSendMessage);// Set send message event

}

 

// When message is received

void COneAtaTimeDlg::OnRecvMessage()

{

    UpdateData(true);

    if(strcmp(kShMem->data,"bye")==0)

        PostQuitMessage(0);

    m_recvlist.AddString(kShMem->data);

    UpdateData(false);

}

 

// When child is started

void COneAtaTimeDlg::OnChildStart()

{

    UpdateData(true);

    m_status = "Child Started...";

    UpdateData(false);

 

}

Top of Form

 

 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

 

 

 

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时进行解答!
以下是Python中多线程和多进程的常用操作: 1. 调用进程: ```python import multiprocessing def worker(): print("This is a worker process") if __name__ == '__main__': p = multiprocessing.Process(target=worker) p.start() p.join() ``` 2. 进程池: ```python import multiprocessing def worker(num): print("This is worker", num) if __name__ == '__main__': pool = multiprocessing.Pool(processes=3) for i in range(3): pool.apply_async(worker, (i,)) pool.close() pool.join() ``` 3. 进程通信(Pipe, Queue): ```python import multiprocessing def sender(conn): conn.send("Hello from sender") conn.close() def receiver(conn): msg = conn.recv() print("Received message:", msg) conn.close() if __name__ == '__main__': parent_conn, child_conn = multiprocessing.Pipe() p1 = multiprocessing.Process(target=sender, args=(child_conn,)) p2 = multiprocessing.Process(target=receiver, args=(parent_conn,)) p1.start() p2.start() p1.join() p2.join() ``` 4. 调用线程: ```python import threading def worker(): print("This is a worker thread") if __name__ == '__main__': t = threading.Thread(target=worker) t.start() t.join() ``` 5. 锁(Lock): ```python import threading counter = 0 lock = threading.Lock() def increment(): global counter with lock: counter += 1 if __name__ == '__main__': threads = [] for _ in range(10): t = threading.Thread(target=increment) t.start() threads.append(t) for t in threads: t.join() print("Counter:", counter) ``` 6. threading.local(): ```python import threading local_data = threading.local() def worker(): local_data.value = 123 print("Worker value:", local_data.value) if __name__ == '__main__': t = threading.Thread(target=worker) t.start() t.join() ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值