利用内存映射文件实现进程间图片轮播

利用内存映射文件实现进程间图片轮播

使用共享内存进行多进程通信是最快的IPC(Inter-Process Communication)方式,往往与信号量配合使用,在另一篇文章当中已经展示过相关的代码,但是可用性不高,因为如果需要向内存写入时可以同时读出数据,会导致展示图片数据错乱。与信号量的配合使用则能够解决这个问题。

首先,C++程序作为图片发送端,即向共享内存中写入图片。轮流地打开imageName数组中的图片写入共享内存区域pBuf,pBuf[0]到pBuf[3]作为信号量而保留,图片数据从pBuf[4]开始写。

图片的读取和流程控制在C#进行,流程中仅使用pBuf[0]与pBuf[1]作为信号量,分别表示是否继续读取和是否完成写入。

  • pBuf[0]为1表示读取继续进行,若不再需要读取,则置pBuf[0]为0,则C++程序会退出while循环,回收共享内存的资源。
  • pBuf[1]为0表示C++程序正在将图片信息写入到内存映射文件,此时C#不应读取其内容,待C++读取完毕置pBuf[1]为1,C#程序则可以开始从内存映射文件中读取数据转换成图片。

将C++程序进行编译,生成应用程序(例如名为readLoop.exe,这需要在C#中指定)然后编写C#的winform工程,并使用BackgroundWorker的多线程方式启动这个应用程序。

#include <cstdio>
#include <cstring>
#include <windows.h>
const char *imageName[] = {
	"11683053441969795147.jpg",
	"0b3cdd7308f340bead962c992b4b2fd4.jpg",
	"18639d5b1f7e40dfbfad6b3bd9c407e9.jpg",
	"37d4448ad3f14b48901ff3b90c33eaea.jpg",
	"495d3b575da64966b3ca3574c3e2c4ea.jpg",
	"589a971d0a7f4814be6926d0076ecaba.jpg",
	"678bf3bfa3554393b2b3e784f257325f.jpg",
	"a4743b26cf0f46dfaf1f42d06611601c.jpg",
	"aaa930ba51e3480c805f8fb82734069b.jpg",
	"d9e43b03a7364fb3849cd3ef0cd840fc.jpg",
	"dd3801e0bb084537b505ca3fcd301f8e.jpg",
	"eac81c422041458c95fb8d8cb31ba1bf.jpg"
	};
#define BUF_SIZE 360000
const char szName[] = "fm01";
int main(void)
{
	char Root[] = "E:\\imgSample\\";
	char fileName[50];
	int idx = 0,i;
	FILE *fp;
	HANDLE hMapFile = CreateFileMapping(
		INVALID_HANDLE_VALUE,
		NULL,
		PAGE_READWRITE,
		0,
		BUF_SIZE,
		(LPCSTR)szName
		);
	char *pBuf = (char*)MapViewOfFile(
		hMapFile,
		FILE_MAP_ALL_ACCESS,
		0,
		0,
		BUF_SIZE
		);
	pBuf[0]=1;
	while(pBuf[0]==1)
	{
		printf("p[0]:%d\t",pBuf[0]);
		strcpy(fileName,Root);
		strcat(fileName,imageName[idx]);
		idx++;
		if(idx>11)
		{
			idx=0;
		}
		printf("%s\n",fileName);
		fp = fopen(fileName,"rb");
		pBuf[1]=0;
		i=4;
		while(fscanf(fp,"%c",&pBuf[i])!=EOF)
		{
			i++;
		}
		pBuf[i]=0;
		pBuf[1]=1;
		Sleep(50);
		fclose(fp);
	}
	UnmapViewOfFile(pBuf);
	CloseHandle(hMapFile);
	printf("end\n");
	return 0;
}
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Threading;
using System.ComponentModel;
using System.Diagnostics;
namespace client_csharp
{
    public partial class Form1 : Form
    {
        const int BUF_SIZE = 360000;
        BackgroundWorker bg1;
        Image pic,lastPic;
        int frameCount;
        byte[] charsInMMf;
        public Form1()
        {
            InitializeComponent();
            bg1 = new BackgroundWorker();
            bg1.WorkerReportsProgress = true;
            bg1.WorkerSupportsCancellation = true;
            bg1.DoWork += new DoWorkEventHandler(doworks);
            bg1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(runcomplete);
            bg1.ProgressChanged += new ProgressChangedEventHandler(progresschanged);
        }
        private void doworks(Object sender,DoWorkEventArgs args)
        {
            Process p = new Process();
            p.StartInfo.FileName = "readLoop.exe";
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.Start();
            lastPic = null;
            waitForOpenFm();
            flush(sender as BackgroundWorker);
        }
        private void waitForOpenFm()
        {
            while (true)
            {
                try
                {
                    MemoryMappedFile.OpenExisting("fm01");
                    break;
                }
                catch
                {
                    // still not open
                    Thread.Sleep(50);
                }
            }
            return;
        }
        private void flush(BackgroundWorker worker)
        {
            using (var mmf = MemoryMappedFile.OpenExisting("fm01"))
            {
                MemoryMappedViewAccessor viewAccessor = mmf.CreateViewAccessor(0, BUF_SIZE);
                charsInMMf = new byte[BUF_SIZE];
                frameCount = 0;
                while (true)
                {
                    while(true)
                    {
                        if (viewAccessor.ReadByte(1)==1)
                        {
                            break;
                        }
                        Thread.Sleep(10);
                    }
                    viewAccessor.ReadArray<byte>(4, charsInMMf, 0, BUF_SIZE);
                    pic = Image.FromStream(new MemoryStream(charsInMMf));
                    worker.ReportProgress(0);
                    frameCount += 1;
                    if (worker.CancellationPending)
                    {
                        viewAccessor.Write(0, 0);
                        break;
                    }
                    Thread.Sleep(50);
                }
            }
        }
        private void runcomplete(Object sender, RunWorkerCompletedEventArgs args)
        {
        }
        private void progresschanged(Object sender, ProgressChangedEventArgs args)
        {
            if(lastPic!=null)
                lastPic.Dispose();
            pictureBox1.BackgroundImage = pic;
            lastPic = pic;
            label1.Text = frameCount.ToString();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            bg1.RunWorkerAsync();
        }
        private void button2_Click(object sender, EventArgs e)
        {
            bg1.CancelAsync();
        }
    }
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注