我已經實作了一個簡單的佇列來存盤檔案名串列,以及一個讀取佇列、獲取下一個可用檔案名并將檔案從一個檔案夾移動到另一個檔案夾的方法。
此類用于跟蹤檔案夾中的檔案。
internal class FileItem
{
public string FullFileName { get; set; }
public bool isLocked { get; set; }
}
這是我的簡單佇列實作
internal class MyQueue
{
List<FileItem> FileList;
public MyQueue(string FilePath)
{
FileList = new List<FileItem>();
string[] files = Directory.GetFiles(FilePath);
foreach (string file in files)
{
FileItem fileitem = new FileItem
{
FullFileName = file,
isLocked = false
};
FileList.Add(fileitem);
}
}
public FileItem GetNextAvailableItem()
{
FileItem item = FileList.Where(i => i.isLocked == false).FirstOrDefault();
if (item != null) item.isLocked = true;
return item;
}
public void RemoveProcessedItem(FileItem item)
{
FileList.Remove(item);
}
}
當我從單執行緒運行它時,它作業正常。
但我正在使用這樣的兩個執行緒。
static void ProcessFilesInMultiThread()
{
Task task1 = Task.Factory.StartNew(() => ReadFromQueueAndMoveFile("Thread 1"));
Task task2 = Task.Factory.StartNew(() => ReadFromQueueAndMoveFile("Thread 2"));
Task.WaitAll(task1, task2);
}
這就是ReadFromQueueAndMoveFile方法。
static void ReadFromQueueAndMoveFile(string ThreadName)
{
while (_queue.GetNextAvailableItem() != null)
{
//get next available item from queue.
FileItem item = _queue.GetNextAvailableItem();
if(item != null)
{
string FileName = Path.GetFileName(item.FullFileName);
string SourceFilePath = Path.Combine(sourcePath, FileName);
string DestinationFilePath = Path.Combine(destinationPath, FileName);
File.Move(SourceFilePath, DestinationFilePath);
Thread.Sleep(2000);
Console.WriteLine("Successfully moved: " FileName " Via " ThreadName);
//remove item from queue.
_queue.RemoveProcessedItem(item);
}
}
}
問題是當我從 2 個執行緒運行它時,總是只有一半的檔案被移動,我不知道為什么。如果檔案夾有 6 個檔案,那么只有 3 個檔案被隨機移動。
為什么會這樣?
uj5u.com熱心網友回復:
我認為你的主要問題在這里:
while (_queue.GetNextAvailableItem() != null)
{
//get next available item from queue.
FileItem item = _queue.GetNextAvailableItem();
您正在呼叫GetNextAvailableItem兩次,并且從第一次呼叫回傳的值被丟棄。
解決此問題的一種方法是:
while (true)
{
//get next available item from queue.
FileItem item = _queue.GetNextAvailableItem();
if (item == null) break;
當然MyQueue,正如 Gabriel 在他們的回答中所建議的那樣,您還應該確保該類是執行緒安全的。
uj5u.com熱心網友回復:
為了防止執行緒相互影響,必須設定一個鎖。為此,該lock宣告很有幫助。請參閱:https ://docs.microsoft.com/en-us/dotnet/csharp/language-reference/statements/lock
為此,定義了一個鎖定物件:
private readonly object fileListLock = new object();
然后在GetNextAvailableItem()方法中使用它:
public FileItem GetNextAvailableItem()
{
lock (fileListLock)
{
FileItem item = FileList.Where(i => i.isLocked == false).FirstOrDefault();
if (item != null) item.isLocked = true;
return item;
}
}
以及RemoveProcessedItem()方法中:
public void RemoveProcessedItem(FileItem item)
{
lock(fileListLock)
{
FileList.Remove(item);
}
}
uj5u.com熱心網友回復:
我能夠使用ConcurrentQueue<T>這解決了我的問題來重寫我的佇列。代碼在這里:
internal class MyQueue
{
ConcurrentQueue<FileItem> FileList = new ConcurrentQueue<FileItem>();
public MyQueue(string FilePath)
{
string[] files = Directory.GetFiles(FilePath);
foreach (string file in files)
{
FileItem fileitem = new FileItem
{
FullFileName = file,
isLocked = false
};
FileList.Enqueue(fileitem);
}
}
public FileItem GetNextAvailableItem()
{
//Deque and return object.
FileItem DequeItem = new FileItem();
bool isDequeSuccess = FileList.TryDequeue(out DequeItem);
if (isDequeSuccess) return DequeItem;
else return null;
}
public bool PeekIfAnyFilesLeftInQueue()
{
FileItem PeekItem = new FileItem();
bool isFileExists = FileList.TryPeek(out PeekItem);
return isFileExists;
}
}
我還必須從多執行緒方法更改這些方法的呼叫。
static void ReadFromQueueAndMoveFile(string ThreadName)
{
do
{
//get next available item from queue.
FileItem item = _queue.GetNextAvailableItem();
if (item != null)
{
string FileName = Path.GetFileName(item.FullFileName);
string SourceFilePath = Path.Combine(sourcePath, FileName);
string DestinationFilePath = Path.Combine(destinationPath, FileName);
// Let's assume this is a long running process.
File.Move(SourceFilePath, DestinationFilePath);
Thread.Sleep(2000);
Console.WriteLine("Successfully moved: " FileName " Via " ThreadName);
}
}
while (_queue.PeekIfAnyFilesLeftInQueue());
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/435497.html
