簡體   English   中英

我可以同時從內存映射文件中讀取(多個線程/進程)而不會阻塞/鎖定嗎?

[英]Can I concurrently read (multiple threads/processes) from a memory mapped file without blocking/locking?

我有一個很大的二進制文件,其中的各個部分需要從不同的線程中同時讀取(根本不需要寫入),並且我試圖啟動一項利用內存映射文件的測試。

我的測試都指出了無法同時訪問和讀取內存映射文件區域的可能性。 n個工人讀取數據所花費的時間呈線性增長。 難道我做錯了什么?

請在下面查看我的代碼段,也許我錯過了一些東西:

這里的代碼片段創建了內存映射文件並保留了對句柄的引用,而其他線程/進程則訪問了內存映射文件區域:

public void CreateNewMemoryMappedFiles(List<TimeSeriesRequest> timeSeriesRequests)
    {
        foreach (var timeSeriesRequest in timeSeriesRequests)
        {
            var header = GetTimeSeriesHeader(timeSeriesRequest.PluginName, timeSeriesRequest.Symbol.SymbolId, timeSeriesRequest.QuoteType, timeSeriesRequest.Compression);

            if (_memoryMappedFileObjects.ContainsKey(header.BinaryFileName))
                continue;

            var pathFileName = _databaseDirectory + "\\" + header.BinaryFileName;

            //create memory mapped file
            var fileStream = File.OpenRead(pathFileName);
            var memoryMappedFile = MemoryMappedFile.CreateFromFile(fileStream, header.BinaryFileName, fileStream.Length, MemoryMappedFileAccess.Read, null, 0, false);

            //create new memory mapped file object and add to collection
            _memoryMappedFileObjects.Add(header.BinaryFileName, new MemoryMappedFileObject(header.BinaryFileName, memoryMappedFile));
        }
    }

在工作程序使用的代碼段的下面(請注意,由於使用不同的線程/進程,這兩個代碼段顯然不駐留在同一類實例中。每個工作人員都有自己的包含以下內容的類實例):

                /read binary data
                BinaryReader reader;
                if (activeBinaryReaders.ContainsKey(header.BinaryFileName))
                {
                    reader = activeBinaryReaders[header.BinaryFileName];
                }
                else
                {
                    var mappedFile = MemoryMappedFile.OpenExisting(header.BinaryFileName, MemoryMappedFileRights.Read, HandleInheritability.None);
                    var viewStream = mappedFile.CreateViewStream(0, 0, MemoryMappedFileAccess.Read);
                    reader = new BinaryReader(viewStream);
                    activeBinaryReaders.Add(header.BinaryFileName, reader);
                }

                //read bytes
                byte[] bytes;
                var indexPositionStart = BinarySearch(dateTimeTuple.Item1, reader, packetSize, true);
                var indexPositionEnd = BinarySearch(dateTimeTuple.Item2, reader, packetSize, false);

                //set starting point in filestream
                reader.BaseStream.Seek(indexPositionStart, SeekOrigin.Begin);

                //read and return byte array
                bytes = reader.ReadBytes((int)(indexPositionEnd - indexPositionStart));

最后,該代碼段模擬了多個試圖同時流傳輸數據的工作程序:

class Program
{
    private const int NumberWorkers = 3;
    private static Stopwatch _watch;

    static void Main(string[] args)
    {
        _watch = new Stopwatch();

        var processor = new ActionBlock<string>(workerId =>
            {
                var tsdb = new TimeSeriesDatabase(@"X:\Algorithmic Trading Static Data\BinaryDatabase");
                var dtFrom = new DateTime(2017, 1, 1, 0, 0, 0);
                var dtTo = new DateTime(2017, 12, 31, 23, 59, 59);
                var request = new TimeSeriesRequest("Me", "DukasCopy", new Symbol("USDJPY", AssetClass.Currency, Currency.JPY), QuoteType.BidAsk, TimeSpan.Zero, dtFrom, dtTo);

                var watch = new Stopwatch();
                var quoteCounter = 0;

                tsdb.StreamDataMemoryMapped(new List<TimeSeriesRequest>() { request }, 2000000, dataPacket =>
                {
                    if (dataPacket.Flag == HistoricalDataStreamFlag.StartUp)
                    {
                        watch.Start();
                    }
                    else if (dataPacket.Flag == HistoricalDataStreamFlag.Packet)
                    {
                        foreach (var quote in dataPacket.Quotes)
                        {

                        }

                        quoteCounter += dataPacket.Quotes.Count;
                    }
                    else if (dataPacket.Flag == HistoricalDataStreamFlag.Shutdown)
                    {
                        watch.Stop();
                        Console.WriteLine($"Worker {workerId} processed {quoteCounter} quotes and took {watch.ElapsedMilliseconds} milliseconds to process");
                    }

                }, new CancellationToken());


            }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = NumberWorkers });


        //create memory mapped file(s)
        var outsideTsdb = new TimeSeriesDatabase(@"X:\Algorithmic Trading Static Data\BinaryDatabase");
        var outsideDtFrom = new DateTime(2017, 1, 1, 0, 0, 0);
        var outsideDtTo = new DateTime(2017, 12, 31, 23, 59, 59);
        var outsideRequest = new TimeSeriesRequest("Me", "DukasCopy", new Symbol("USDJPY", AssetClass.Currency, Currency.JPY), QuoteType.BidAsk, TimeSpan.Zero, outsideDtFrom, outsideDtTo);
        outsideTsdb.CreateNewMemoryMappedFiles(new List<TimeSeriesRequest>(){outsideRequest});

        foreach (var workerIndex in Enumerable.Range(1, NumberWorkers))
        {
            processor.Post($"Worker {workerIndex}");
        }

        _watch.Start();

        //mark completion
        processor.Complete();
        processor.Completion.Wait();

        //print out results
        _watch.Stop();
        Console.WriteLine($"{NumberWorkers} workers took {_watch.ElapsedMilliseconds} milliseconds in total.");

        Console.WriteLine("Press button to stop");
        Console.ReadLine();
    }
}

在這種過程中,好的做法是將文件的副本創建為臨時文件。

例:

 // YOUR CODE
 var pathFileName = _databaseDirectory + "\\" + header.BinaryFileName;
 var tempPathFilename = "temporarypath" + "\\" + header.BinaryFileName;

tempPathFilename

以編程方式將原始文件復制到臨時路徑文件名目錄中。

每當您要訪問該文件時,請對該文件進行臨時復制並處理該臨時文件(而不是原始文件),以避免阻塞。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM