原因
在unity编辑器下打算用多线程来查找处理里面的资源,但又想阻塞主线程,防止用户操作unity,又能准确显示多线程的进度。在分析unity编辑器的使用多线程示例后,发现可以用 ManualResetEvent 信号来通知主线程。
解决
主线程阻塞掉,直到信号通知继续执行下面的操作。另外,保证多线程的数量跟处理器数量一致,最大化使用多线程。
#region 多线程执行
private class JobFileTextSearchReplace
{
private string m_Path;
private List<string> m_Patterns;
private string m_ReplaceStr;
private bool[] m_SearchRet;
public ManualResetEvent doneEvent;
public string exception;
public JobFileTextSearchReplace(string path, List<string> patterns, string replaceStr, bool[] searchRet)
{
m_Path = path;
m_Patterns = patterns;
m_ReplaceStr = replaceStr;
m_SearchRet = searchRet;
doneEvent = new ManualResetEvent(false);
}
public void ThreadPoolCallback(System.Object threadContext)
{
try
{
string text = File.ReadAllText(m_Path);
///
}
catch (Exception ex)
{
exception = m_Path + "\n" + ex.Message;
}
doneEvent.Set();
}
}
protected void ThreadDoFilesTextSearchReplace(List<string> fileList, List<string> patterns, string replaceStr, bool[][] searchRetList)
{
List<JobFileTextSearchReplace> jobList = new List<JobFileTextSearchReplace>();
List<ManualResetEvent> eventList = new List<ManualResetEvent>();
int numFiles = fileList.Count;
int numFinished = 0;
AssetDanshariUtility.DisplayThreadProgressBar(numFiles, numFinished);
int timeout = 600000; // 10 分钟超时
for (var i = 0; i < fileList.Count; i++)
{
JobFileTextSearchReplace job = new JobFileTextSearchReplace(fileList[i], patterns, replaceStr, searchRetList[i]);
jobList.Add(job);
eventList.Add(job.doneEvent);
ThreadPool.QueueUserWorkItem(job.ThreadPoolCallback);
if (eventList.Count >= Environment.ProcessorCount)
{
WaitForDoFile(eventList, timeout);
AssetDanshariUtility.DisplayThreadProgressBar(numFiles, numFinished);
numFinished++;
}
}
while (eventList.Count > 0)
{
WaitForDoFile(eventList, timeout);
AssetDanshariUtility.DisplayThreadProgressBar(numFiles, numFinished);
numFinished++;
}
foreach (var job in jobList)
{
if (!string.IsNullOrEmpty(job.exception))
{
Debug.LogError(job.exception);
}
}
}
private void WaitForDoFile(List<ManualResetEvent> events, int timeout)
{
int finished = WaitHandle.WaitAny(events.ToArray(), timeout);
if (finished == WaitHandle.WaitTimeout)
{
// 超时
}
events.RemoveAt(finished);
}
#endregion