Рейтинг:
6
Richard Deeming
Прерывание нити-это очень опасная вещь, и ее следует избегать любой ценой.
Вы используете BackgroundWorker
, который уже выполняет работу в фоновом потоке. Начало другой поток, чтобы сделать эту работу, а затем заморозить начальный фоновый поток до тех пор, пока ваш новый поток не закончит, будет только уменьшать производительность вашего кода.
Забудьте о ручном нарезании резьбы и выполняйте работу в ручном режиме. DoWork
обработчик событий.
Набор то WorkerSupportsCancellation
собственность[^] к true
Если вы хотите остановить работника, позвоните ему то CancelAsync
метод[^]. Внутри вашего цикла проверьте то CancellationPending
собственность[^] чтобы увидеть, была ли работа отменена, и остановить, если она была отменена.
private void Abort_Click(object sender, EventArgs e)
{
backgroundCopy.CancelAsync();
}
private void backgroundCopy_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
List<object> genericlist = e.Argument as List<object>;
if (genriclist is null || genericlist.Count < 2) throw new ArgumentException();
string source = Convert.ToString(genericlist[0]);
string destination = Convert.ToString(genericlist[1]);
// Do your work here, periodically checking the CancellationPending flag:
if (backgroundCopy.CancellationPending)
{
e.Cancel = true;
return;
}
}
Richard Deeming
Почему вы определяете свойства в своем классе вместо того, чтобы устанавливать их на BackgroundWorker
?
Уберите линии:
public bool WorkerSupportsCancellation { get; set; }
[System.ComponentModel.Browsable(false)]
public bool CancellationPending { get; }
Выберите пункт
BackgroundWorker
в конструкторе откройте его свойства и найдите
WorkerSupportsCancellation
собственность. Установите его в положение
true
.
Уберите линии:
Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].ToString()));
thread1.Start();
thread1.Join();
Либо переместите код из вашего
Copy
метод в
backgroundCopy_DoWork
метод, или просто вызовите
Copy
метод непосредственно, без запуска другого потока.
В пределах кода, выполняющего копирование, периодически проверяйте
backgroundCopy.CancellationPending
свойство и остановка, если оно возвращается
true
.
Richard Deeming
Потому что у вас нет элемента управления под названием cancelButton
на форме.
Удалите этот метод; в нем нет необходимости, так как вы уже отменяете работу из Abort_Click
обработчик.
AskalotLearnalot
мне очень жаль, что у меня все еще есть проблемы после этого.
Richard Deeming
Так ты вообще не читал мой ответ?
Не используйте нить.
Регистрация backgroundCopy.CancellationPending
периодически во время копирования, и прекратите копирование, если он вернется true
.
AskalotLearnalot
Спасибо, что уделили мне время.
private void Abort_Click(object sender, EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundCopy.CancelAsync();
// Disable the Cancel button.
Abort.Enabled = false;
cancel = !cancel;
}
/// Runs method 'Copy' in a new thread with passed arguments - on this way we separate it from UI thread otherwise it would freeze
private void backgroundCopy_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//List<object> genericlist = e.Argument as List<object>;
//Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].ToString()));
//thread1.Start();
//thread1.Join(); //Waiting for thread to finish
List<object> genericlist = e.Argument as List<object>;
if (genericlist is null || genericlist.Count < 2) throw new ArgumentException();
string source = Convert.ToString(genericlist[0]);
string destination = Convert.ToString(genericlist[1]);
if (backgroundCopy.CancellationPending)
{
e.Cancel = true;
return;
}
}
У меня есть это, но я не могу понять, как вызвать метод копирования, как это делается в:
//Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].Метод toString()));
Richard Deeming
Вы не можете понять, как вызвать метод в C#?
Copy(source, destination);
Но еще раз вам нужно будет проверить
backgroundCopy.CancellationPending
свойство периодически внутри вашего
Copy
метод, и остановить копирование, если он возвращает
true
.
AskalotLearnalot
О я был напуган этой строкой:
//Thread thread1 = new Thread(() => Copy(genericlist[0].ToString(), genericlist[1].ToString()));
Вот что я сделал в методе копирования, но он все еще не прерывается, я уверен, что сделал что-то не так.
public void Copy(string sourceDirectory, string targetDirectory)
{
if (backgroundCopy.CancellationPending != true)
{
//Do code
}
else
{
MessageBox.Show("Progrss terminated");
return;
}
}
Richard Deeming
Вам нужно периодически проверять флаг во время части "Do code", а не только один раз в начале.
Как вы это сделаете, будет зависеть от того, как выглядит часть "Do code".
AskalotLearnalot
это метод копирования:
public void Copy(string sourceDirectory, string targetDirectory)
{
if (backgroundCopy.CancellationPending != true)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);
string[] entries = null;
try
{
if (!diSource.ToString().Contains("System Volume Information") && !diSource.ToString().ToUpper().Contains("$RECYCLE.BIN"))
{
//Gets list of all files and directories (we need it for progress bar)
entries = GetFiles(sourceDirectory, "*").ToArray();
}
}
catch (UnauthorizedAccessException ex)
{
//ok, so we are not allowed to dig into that directory. Move on.
}
int max = 1;
if (entries != null && entries.Count() != 0)
{
max = entries.Count();
}
//Using Invoke to prevent Cross thread exception
Invoke(new Action(() => progressBar1.Maximum = max));
Invoke(new Action(() => progressBar1.Step = 1));
Invoke(new Action(() => progressBar1.Value = 0));
CopyAll(diSource, diTarget, entries);
Cursor.Current = Cursors.Default;
}
else
{
MessageBox.Show("Progrss terminated");
return;
}
}
У меня есть этот метод CopyAll, который, как я думаю, я должен поместить в него проверку отмены:
public void CopyAll(DirectoryInfo newsource, DirectoryInfo newtarget, string[] entries)
{
//if (cancel == true)
//{
// return;
//}
Cursor.Current = Cursors.WaitCursor;
Application.DoEvents();
Directory.CreateDirectory(newtarget.FullName);
//cancel = false;
//while (cancel != true)
//{
// Copy each file into the new directory.
foreach (FileInfo fi in newsource.GetFiles())
{
if (!fi.ToString().Contains("System Volume Information") && !fi.ToString().ToUpper().Contains("$RECYCLE.BIN"))
{
Invoke(new Action(() => lblInfo.Text = "Stopped"));
//Using Invoke to prevent Cross thread exception
Invoke(new Action(() => this.lblInfo.Text = string.Format("{0}\\{1}", newsource.FullName, fi.Name)));
if (File.Exists(Path.Combine(newtarget.FullName, fi.Name)))
{
File.SetAttributes(Path.Combine(newtarget.FullName, fi.Name), FileAttributes.Normal);
File.Delete(Path.Combine(newtarget.FullName, fi.Name));
}
fi.CopyTo(Path.Combine(newtarget.FullName, fi.Name), true);
File.SetAttributes(Path.Combine(newtarget.FullName, fi.Name), FileAttributes.Normal);
Application.DoEvents();
var prval = Math.Min(progressBar1.Maximum, progressBar1.Value + 1);
Invoke(new Action(() => progressBar1.Value = prval));
}
}
// Copy each subdirectory using recursion.
foreach (DirectoryInfo diSourceSubDir in newsource.GetDirectories())
{
try
{
if (!diSourceSubDir.ToString().Contains("System Volume Information") && !diSourceSubDir.ToString().ToUpper().Contains("$RECYCLE.BIN"))
{
DirectoryInfo ne
Richard Deeming
Во-первых, набор backgroundCopy.WorkerReportsProgress
к true
, и использовать ReportProgres
способ и ProgressChanged
событие для обновления индикатора выполнения. Тогда вам не нужно будет использовать Invoke
.
private void backgroundCopy_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBar1.Maximum = (int)e.UserState;
progressBar1.Step = 1;
progressBar1.Value = e.ProgressPercentage;
}
public void Copy(string sourceDirectory, string targetDirectory)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
if (!diSource.Exists) return;
if (diSource.FullName.Contains("System Volume Information")) return;
if (diSource.FullName.Contains("$RECYCLE.BIN")) return;
string[] entries;
try
{
entries = Directory.GetFiles(diSource.FullName, "*");
}
catch (UnauthorizedAccessException)
{
return;
}
backgroundCopy.ReportProgress(0, entries.Length);
CopyAll(diSource, diTarget, entries);
Cursor.Current = Cursors.Default;
}
Теперь вам нужно проверить
backgroundCopy.CancellationPending
флаг периодически внутри вашего
CopyAll
метод.
Richard Deeming
public void CopyAll(DirectoryInfo newsource, DirectoryInfo newtarget, string[] entries)
{
Cursor.Current = Cursors.WaitCursor;
newtarget.Create();
foreach (FileInfo fi in newsource.GetFiles())
{
// Check whether the copy has been cancelled:
if (backgroundCopy.CancellationPending) return;
if (fi.FullName.Contains("System Volume Information")) continue;
if (fi.FullName.Contains("$RECYCLE.BIN")) continue;
// Copy the file
...
}
foreach (DirectoryInfo diSourceSubDir in newsource.GetDirectories())
{
// Check whether the copy has been cancelled:
if (backgroundCopy.CancellationPending) return;
if (diSourceSubDir.FullName.Contains("System Volume Information")) continue;
if (diSourceSubDir.FullName.Contains("$RECYCLE.BIN")) continue;
try
{
string entries = Directory.GetFiles(diSource.FullName, "*");
DirectoryInfo diTargetSubDir = new DirectoryInfo(Path.Combine(newtarget.FullName, diSourceSubDir.Name));
CopyAll(diSourceSubDir, diTargetSubDir, entries);
}
catch (UnauthorizedAccessException)
{
}
}
}
AskalotLearnalot
Спасибо вам за гораздо более эффективный код, который показывает разницу в опыте. Теперь моя проблема заключается в копии всего, что я знаю, только как проверить это так, как я показал в предыдущем комментарии с If{} else{} Я не знаю, как делать это постоянно, прежде чем каждый файл или папка будут скопированы.
Richard Deeming
Просто положи трубку. if
внутри дома foreach
блоки. Таким образом, он будет выполняться для каждого файла и каталога.
AskalotLearnalot
Вот что у меня есть. Он действительно завершает процесс, однако окно сообщения показывает около 6 раз, а затем останавливается. и прогресс останавливается.
public void CopyAll(DirectoryInfo newsource, DirectoryInfo newtarget, string[] entries)
{
//if (cancel == true)
//{
// return;
//}
Cursor.Current = Cursors.WaitCursor;
Application.DoEvents();
Directory.CreateDirectory(newtarget.FullName);
//cancel = false;
//while (cancel != true)
//{
// Copy each file into the new directory.
foreach (FileInfo fi in newsource.GetFiles())
{
if (backgroundCopy.CancellationPending != true)
{
if (!fi.ToString().Contains("System Volume Information") && !fi.ToString().ToUpper().Contains("$RECYCLE.BIN"))
{
Invoke(new Action(() => lblInfo.Text = "Stopped"));
//Using Invoke to prevent Cross thread exception
Invoke(new Action(() => this.lblInfo.Text = string.Format("{0}\\{1}", newsource.FullName, fi.Name)));
if (File.Exists(Path.Combine(newtarget.FullName, fi.Name)))
{
File.SetAttributes(Path.Combine(newtarget.FullName, fi.Name), FileAttributes.Normal);
File.Delete(Path.Combine(newtarget.FullName, fi.Name));
}
fi.CopyTo(Path.Combine(newtarget.FullName, fi.Name), true);
File.SetAttributes(Path.Combine(newtarget.FullName, fi.Name), FileAttributes.Normal);
Application.DoEvents();
var prval = Math.Min(progressBar1.Maximum, progressBar1.Value + 1);
Invoke(new Action(() => progressBar1.Value = prval));
}
}
else
{
MessageBox.Show("Progress have been cancelled");
return;
}
}
// Copy each subdirectory using recursion.
foreach (DirectoryInfo diSourceSubDir in newsource.GetDirectories())
{
if (backgroundCopy.CancellationPending != true)
{
try
{
if (!diSourceSubDir.ToString().Contains("System Volume Information") && !diSourceSubDir.ToString().ToUpper().Contains("$RECYCLE.BIN"))
{
DirectoryInfo nextTargetSubDir =
newtarget.CreateSubdirectory(diSourceSubDir.Name);
CopyAll(diSourceSubDir, nextTargetSubDir, entries);
var prval = Math.Min(progressBar1.Maximum, progressBar1.Value + 1);
Invoke(new Action(() => progressBar1.Value = prval));
}
else
{
var prval = Math.Min(progressBar1.Maximum, progressBar1.Value + 1);
Invoke(new Action(() => progressBar1.Value = prval));
}
}
catch (Exception ex) { MessageBox.Show(ex.Message); }
}
else
{
MessageBox.Show("Progress have been cancelled");
return;
}
}
}
AskalotLearnalot
Также боковое примечание: что касается нового метода копирования, который вы предоставили, индикатор выполнения по какой-то причине останавливается посередине, даже если прогресс выполнен, поэтому я отредактировал его таким образом:
public void Copy(string sourceDirectory, string targetDirectory)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);
if (!diSource.Exists) return;
if (diSource.FullName.Contains("System Volume Information")) return;
if (diSource.FullName.Contains("$RECYCLE.BIN")) return;
string[] entries;
try
{
entries = Directory.GetFiles(diSource.FullName, "*");
}
catch (UnauthorizedAccessException)
{
return;
}
int max = 1;
if (entries != null && entries.Count() != 0)
{
max = entries.Count();
}
Invoke(new Action(() => progressBar1.Maximum = max));
Invoke(new Action(() => progressBar1.Step = 1));
Invoke(new Action(() => progressBar1.Value = 0));
int percent = (int)(((double)progressBar1.Value / (double)progressBar1.Maximum) * 100);
progressBar1.CreateGraphics().DrawString(percent.ToString() + "%", new Font("Arial", (float)8.25, FontStyle.Regular), Brushes.Black, new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
backgroundCopy.ReportProgress(0, max);
CopyAll(diSource, diTarget, entries);
Cursor.Current = Cursors.Default;
}