#c# #multithreading
#c# #многопоточность
Вопрос:
Приведенный ниже код завершается ошибкой «Процесс не может получить доступ к файлу, поскольку он используется другим процессом». Я в замешательстве относительно того, что не так. Я запускаю Visual Studio от имени администратора, и ни один из файлов не открыт в блокноте.
private void Load_Click(object sender, RoutedEventArgs e)
{
if (txtInput.Text.Length > 1) {
//var rootDir = System.IO.Directory.GetCurrentDirectory();
string rootDir = @"C:b";
string search = txtInput.Text.Replace(" ", "");
List<Thread> searches = new List<Thread>();
foreach (var file in new DirectoryInfo(rootDir).GetFiles().Where(z => z.LastWriteTime > DateTime.Now.AddDays(-7))) {
if (file.ToString().Contains(".log")) {
searches.Add(new Thread(new ThreadStart(() => AddDropdownItem(file.ToString(),search))));
}
}
//Run ten threads at a time and wait for them to finish
for (int i = 0; i < searches.Count; i = i 10) {
List<Thread> pool = new List<Thread>();
for (int j = 0; j < 10; j ) {
if (i j < searches.Count) {
Thread t = searches[(i j)];
pool.Add(t);
}
}
foreach (Thread t in pool) {
t.Start();
}
foreach (Thread t in pool) {
t.Join();
}
}
}
}
private void AddDropdownItem(string file, string search)
{
if (GetFileContent(file.ToString()).Contains(search)) {
ComboBoxItem item = new ComboBoxItem();
item.Content = file.ToString();
Dispatcher.BeginInvoke(new ThreadStart(() => ddFiles.Items.Add(item)));
}
}
private string GetFileContent(string file)
{
string path = System.IO.Path.Combine(@"C:b", file);
using (FileStream fs = new FileStream(path, FileMode.Open)) {
return new StreamReader(fs).ReadToEnd();
}
}
Комментарии:
1. 1 в качестве поддержки против не прокомментированного снижения
2. почему ты звонишь. toString() в ваших строках?
Ответ №1:
Проблема, скорее всего, связана с тем, как вы фиксируете переменную цикла в лямбда-выражении. Помните, что замыкания фиксируют переменную, а не значение. Таким образом, в принципе, AddDropdownItem
метод может получать другое значение для параметра file
, чем вы думаете. Это хорошо известное поведенческое предостережение при закрытии переменной цикла.
Измените цикл таким образом, чтобы скопировать переменную цикла в отдельную ссылку.
foreach (var file in new DirectoryInfo(rootDir).GetFiles().Where(z => z.LastWriteTime > DateTime.Now.AddDays(-7)))
{
if (file.ToString().Contains(".log"))
{
var capture = file;
searches.Add(new Thread(new ThreadStart(() => AddDropdownItem(capture.ToString(),search))));
}
}
Я заметил потенциальную несвязанную проблему. Похоже, что вы создаете ComboBoxItem
из одного из своих рабочих потоков. Я вижу, что вы маршалируете операцию добавления в поток пользовательского интерфейса. Я бы убедился, что ComboBoxItem
также создается в потоке пользовательского интерфейса для хорошей оценки. То, что у вас есть, вероятно, не вызовет никаких проблем, но я бы перестраховался. Я склонен доводить правило о запрете доступа к элементам пользовательского интерфейса из потока, отличного от потока пользовательского интерфейса, до крайнего предела.
Комментарии:
1. Это идеально! Ваш первый ответ решил мою проблему, затем второй решил другую ошибку, которую я обнаружил, когда она начала работать. Высоко ценится
Ответ №2:
Я не вижу ‘AddDropDownItem’, но я готов поспорить, что вы открываете файл там и не закрываете файлы, когда поток с ними закончен. Освободить переменные (или просто позволить им выпадать из области видимости и позволить GC обрабатывать это) недостаточно. Сначала явно закройте файлы, прежде чем поток завершится.
Комментарии:
1. Весь код приведен в примере выше. Я поместил поток файлов в блок using. Я также только что попытался явно закрыть его, но это не помогло
2. Вы открываете какие-либо другие файлы где-то еще?