我目前正在从事一个个人项目,旨在管理文件集合。文件被分类到库中并存储在文件夹中。应用启动时,我想将所有文件加载到内存中并为其分配唯一的ID。由于id仅在运行时很重要,因此我使用一个简单的整数,该整数在加载新文件时会递增。
文件夹结构如下:
- AppFolder
- LibraryFolder1
- FileFolder11
- File111
- File112
- FileFolder12
- File121
- File122
.
.
.
- LibraryFolder2
- FileFolder21
.
.
.
为了增加启动时间,我使用了多线程方法。每个线程都被分配了库文件夹的一部分,并从那里启动了其他线程,这些线程在FileFolders的一部分上工作。 通过这种方法,我获得了显着的性能提升,因此我希望朝这个方向继续。
不幸的是,该代码似乎有一个错误,因为在某些运行中,将id分配了两次,因此我在以id为键将它们添加到Dictionary时遇到了错误。
通过调用LoadLibraries()
方法开始加载过程。
public class LibraryManager
{
private IDictionary<int, Library> mLibraries;
public IDictionary<int, Library> Libraries
{
get { return mLibraries; }
private set { mLibraries = value; }
}
private int IDCount = 0;
private static LibraryManager mInstance;
public static LibraryManager Instance
{
get
{
if (mInstance == null)
{
mInstance = new LibraryManager();
}
return mInstance;
}
}
private LibraryManager()
{
Libraries = new ConcurrentDictionary<int, Library>();
}
/// <summary>
/// Creates a new Library id. This method uses an auto increment method.
/// </summary>
/// <returns>unique runtime id for a library</returns>
private int GetNewID()
{
return Interlocked.Increment(ref IDCount);
}
/// <summary>
/// Reads all libraries in the libraries folder and creates
/// corresponding Library objects.
/// Library folders must contain a library.nfo file.
/// </summary>
public void LoadLibraries(string libraryFolderPath)
{
Libraries.Clear();
string[] libraryFolders = Directory.GetDirectories(libraryFolderPath);
int threadCount = Math.Max(1, Math.Min(libraryFolders.Length / 25, Environment.ProcessorCount));
int batchSize = (int)Math.Ceiling((double)libraryFolders.Length / threadCount);
for (int i = 0; i < threadCount; i++)
{
int start = batchSize * i;
int end = Math.Min((i + 1) * batchSize, libraryFolders.Length);
Thread thread = new Thread(() => LoadLibraries(libraryFolders, start, end));
thread.IsBackground = true;
thread.Start();
}
}
/// <summary>
/// Loads a section of the library folders from the app folder.
/// </summary>
/// <param name="libraryFolders">list of paths to library folders</param>
/// <param name="start">start index</param>
/// <param name="end">end index</param>
private void LoadLibraries(string[] libraryFolders, int start, int end)
{
for (int i = start; i < end; i++)
{
Library library = new Library(GetNewID());
library.LoadFiles(libraryFolders[i]);
Libraries.Add(library.ID, library);
}
}
}
public class Library
{
private IDictionary<int, FileObject> mFileObjects;
public IDictionary<int, FileObject> FileObjects
{
get { return mFileObjects; }
private set { mFileObjects = value; }
}
private int mID;
public int ID
{
get { return mID; }
private set { mID = value; }
}
public Library(int id)
{
ID = id;
FileObjects = new ConcurrentDictionary<int, FileObject>();
}
/// <summary>
/// Checks whether this library contains a fileObject with the same id.
/// </summary>
/// <param name="id">id which is searched for</param>
/// <returns>true if a fileObject with the same id is already added to this library</returns>
public bool Contains(int id)
{
return FileObjects.ContainsKey(id);
}
/// <summary>
/// Checks whether this library contains the given file object.
/// </summary>
/// <param name="fileObject">fileObject which is searched for</param>
/// <returns>true if the fileobject is already in the library</returns>
public bool Contains(FileObject fileObject)
{
return Contains(fileObject.ID);
}
/// <summary>
/// Loads all files currently created for this library.
/// </summary>
public void LoadFiles(string libraryFolder)
{
string[] fileFolders = Directory.GetDirectories(libraryFolder);
int threadCount = Math.Max(1, Math.Min(fileFolders.Length / 2, Environment.ProcessorCount));
int batchSize = (int)Math.Ceiling((double)fileFolders.Length / threadCount);
for (int i = 0; i < threadCount; i++)
{
int start = batchSize * i;
int end = Math.Min((i + 1) * batchSize, fileFolders.Length);
Thread thread = new Thread(() => LoadFiles(fileFolders, start, end));
thread.IsBackground = true;
thread.Start();
}
}
/// <summary>
/// Loads a section of the file folders from the FileFolder.
/// </summary>
/// <param name="fileFolders ">list of all file folders</param>
/// <param name="start">start index</param>
/// <param name="end">end index</param>
private void LoadFiles(string[] fileFolders, int start, int end)
{
for (int i = start; i < end; i++)
{
FileObject fileObject = FileManager.Instance.CreateFileObject();
AddFileObject(fileObject);
}
}
/// <summary>
/// Adds a fileObject to this library if it is not already added.
/// </summary>
/// <param name="fileObject">file object to add</param>
private void AddFileObject(FileObject fileObject)
{
if (!(fileObject == null || Contains(fileObject))) // (1)
{
fileObject.Library = this;
FileObjects.Add(fileObject.ID, fileObject); // (2)
}
}
}
public class FileManager
{
private int IDCount = 0;
private static FileManager mInstance;
public static FileManager Instance
{
get
{
if (mInstance == null)
{
mInstance = new FileManager();
}
return mInstance;
}
}
private FileManager()
{
}
/// <summary>
/// Creates a new FileObject id. This method uses an auto increment method.
/// </summary>
/// <returns>unique runtime id for a file object</returns>
private int GetNewID()
{
return Interlocked.Increment(ref IDCount);
}
public FileObject CreateFileObject()
{
return new FileObject(GetNewID());
}
}
public class FileObject
{
private int mID;
public int ID
{
get { return mID; }
private set { mID = value; }
}
private Library mLibrary;
public Library Library
{
get { return mLibrary; }
set { mLibrary = value; }
}
public FileObject(int id)
{
ID = id;
}
}
class Program
{
static void Main(string[] args)
{
LibraryManager.Instance.LoadLibraries(Path to AppFolder);
Console.ReadLine(); // wait for threads to run
}
}
据我所知,代码中的标记部分(// (1)
或// (2)
)发生了错误。
Contains(FileObject f)
失败是因为已经添加了另一个具有相同ID的对象,或者词典引发了重复的输入异常。
看来这是一种竞争状况,但我不知道如何解决。
如果有人能分享一些见解,我将不胜感激。