To stop The thread based on a flag Finesedequeue In the below thread method I did a loop in the stop button(its purpose is to stop this thread after it fills the queue named _textFromFilesQueue ) and the main problem in this is that my form is freezing
Here is my code:
The Threads Method:
public static bool Finisedqueue ;
public void Read(string inputDirectoryPath)
{
try
{
Thread.CurrentThread.IsBackground = true;
Finisedqueue = false;
Console.WriteLine("thread 1 started");
if (Form1.IsStarted)
{
Thread.Sleep(3000);
var dir = new DirectoryInfo(inputDirectoryPath);
FileInfo[] f = dir.GetFiles("*.txt");
if (f.Length > 0)
{
Console.WriteLine("{0} directory contains {1} Files",inputDirectoryPath,f.Length);
int counter = 0;
foreach (System.IO.FileInfo fi in f)
{
counter++;
var filePath = Path.Combine(fi.DirectoryName, fi.Name);
string textFromFile = File.ReadAllText(filePath);
_textFromFilesQueue.Enqueue(textFromFile);
Console.WriteLine("The text inside file number {0} is : {1}",counter,textFromFile);
fi.Delete();
Console.WriteLine("deleting file number {0} from input folder",counter);
}
Finisedqueue = true;
Console.WriteLine("finished Deleting files");
}
else
{
Console.WriteLine("{0} Has no files inside it ",inputDirectoryPath);
}
}
}
catch (Exception excep)
{
Console.WriteLine("An error occurred: '{0}'", excep);
throw(excep);
}
}
The Stop Button Click:
private void stop_Click(object sender, EventArgs e)
{
while (!Thread1.Finisedqueue)
{
Console.WriteLine("Queue from First Thread is still Enqueuing data");
}
Thread1Timer.Dispose();
button3.Enabled = true;
textBox3.Enabled = true;
}
> the main problem in this is that my form is freezing
IF any body can help me in this Thank you very much
When busy waiting it is a good idea to sleep, so that you give other threads a chance to execute.
while (!Thread1.Finisedqueue)
{
Thread.Sleep(100);
Console.WriteLine("Queue from First Thread is still Enqueuing data");
}
You can also check out the Task class
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx which is nice for asynchronous operations such as file IO and is awaitable using the built-in async/await functionality.
private Queue<string> q = new Queue<string>();
private Task readFilesTask;
public void ReadFiles(string inputDirectory)
{
var files = Directory.GetFiles(inputDirectory, "*.txt", SearchOption.TopDirectoryOnly);
foreach (var file in files)
{
var textFromFile = File.ReadAllText(file);
q.Enqueue(textFromFile);
File.Delete(file);
}
}
private void start_Click(object sender, EventArgs e)
{
readFilesTask = Task.Run(() => ReadFiles("//path/to/dir"));
}
private void stop_Click(object sender, EventArgs e)
{
readFilesTask.Wait();
//readFilesTask.Wait(1000); // if you want kill the task after waiting for 1000 milliseconds
//Enable buttons
}
Related
The BackgroundWorker's progressbar is not updated while doing some tasks. What I would like to reach is progressbar moving while iterating through each file in DirectoryInfo. Suppose we have 20 files of ".sql" while first file completed it should be 5%, 10% and etc.
Here is my code.
private void CSV_Click(object sender, RoutedEventArgs e)
{
try
{
btnExtract.IsEnabled = false;
workerextract.RunWorkerAsync();
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
this.Dispatcher.Invoke(() =>
{
DirectoryInfo di = new DirectoryInfo(txtQueryfolder.Text);
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
//System.Windows.MessageBox.Show(line);
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
});
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private void workerextract_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBarExtract.Value = e.ProgressPercentage;
}
private void workerextract_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnExtract.IsEnabled = true;
System.Windows.MessageBox.Show("CSV Data extraction finished!");
}
I found that
private void workerextract_ProgressChanged(object sender,
System.ComponentModel.ProgressChangedEventArgs e)
is called once at the end when 100%.
Also,
private void workerextract_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
never called as I do not see Message Box at the end.
So, I think I am doing something wrong here, could you please direct me on right way?
The problem was in wrapping whole DoWork inside Dispatcher.Invoke.
I need to wrap only those code where it is interacting with UI.
So I changed the code appropriately and it works.
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
this.Dispatcher.Invoke(() =>
{
di = new DirectoryInfo(txtQueryfolder.Text);
});
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
this.Dispatcher.Invoke(() =>
{
//System.Windows.MessageBox.Show(line);
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
});
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
Thanks to all for showing the direction.
Using this.Dispatcher.Invoke in the BackgroundWorker's DoWork event you are executing the whole operation in the UI thread; which is what BackgroundWorker born to avoid to do.
Also, you get an error unwrapping your code from the dispatcher because you are accessing an UI object, which is txtQueryfolder.
Just use:
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
string queryFolder = e.Argument.ToString();
try
{
DirectoryInfo di = new DirectoryInfo(queryFolder);
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
//System.Windows.MessageBox.Show(line);
// ExtractToCSV shouldn't access to a UI object.
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
}
catch (Exception ex)
{
// Don't use MessageBox in a thread different from the UI one. Just set the result (e.Result) and get that in the RunWorkerCompleted event.
// System.Windows.MessageBox.Show(ex.Message);
}
}
When you call the RunWorkerAsync method just add the parameter like below:
workerextrac.RunWorkerAsync(txtQueryfolder.Text);
it's my first question I'm asking here, so please be gentle with me ;)
So I've actually got two WinForms in my C# application I'm writing at the moment (I'm quite new to C#).
This window has a button, which saves photos from an usb device you selected before in a list box to another folder.
After clicking on this button my main thread is of course busy with copying, so I decided to create another WinForm which contains my ProgressBar.
Foreach completed copy, I want to increment my ProgressBar accordingly.
So I count the number of copies I have to do and give it the progressbar as maximum. But my problem at the moment is, that I really don't know how to increment the ProgressBar without getting a Thread Unsafe Exception.
Here's my ProgressWindow code:
public partial class ProgressWindow : Form
{
BackgroundWorker updateProgressBarThread = new BackgroundWorker();
private Boolean _isThreadRunning = false;
public Boolean IsThreadRunning
{
get { return _isThreadRunning; }
set { _isThreadRunning = value; }
}
private int _progressbarLength;
public int ProgressbarLength
{
get { return _progressbarLength; }
set { _progressbarLength = value; }
}
private int progress = 1;
public ProgressWindow()
{
Show();
InitializeComponent();
}
private void StartUpdateThread(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Reports progress to the ProgressChangedEvent function. (Thread Safe)
}
private void FinishProgressThread(object sender, RunWorkerCompletedEventArgs e)
{
if (!_isThreadRunning)
{
MessageBox.Show("Erfolgreich kopiert");
Close();
}
}
private void ProgressChangedEvent(object sender, ProgressChangedEventArgs e)
{
this.copyProgressbar.Value = e.ProgressPercentage;
this.progressStatus.Text = e.ProgressPercentage.ToString();
}
public void CallUpdateThread()
{
updateProgressBarThread.WorkerReportsProgress = true;
updateProgressBarThread.DoWork += new DoWorkEventHandler(StartUpdateThread);
updateProgressBarThread.ProgressChanged += new ProgressChangedEventHandler(ProgressChangedEvent);
updateProgressBarThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(FinishProgressThread);
updateProgressBarThread.RunWorkerAsync();
}
}
I want to increment my ProgressBar with 1 after each succesful copy.
How do I do this from my main thread?
This is the function which actually handles the copy process
private void SaveFile(System.IO.DirectoryInfo root)
{
try
{
IEnumerable<DirectoryInfo> directoriesNames = root.EnumerateDirectories();
// New instance of thread ProgressWindow.
ProgressWindow progress = new ProgressWindow();
progress.CallUpdateThread();
foreach (DirectoryInfo element in directoriesNames)
{
// Query all subdirectories and count everything with the in the configuration made settings.
if (!element.Attributes.ToString().Contains("System"))
{
// Now we insert the configuration we applied.
String fileExtension = null;
if (Properties.Settings.Default._configPhoto)
{
fileExtension = "*.jpg";
}
if (Properties.Settings.Default._configWordDocument)
{
fileExtension = "*.odt";
}
FileInfo[] jpgList = element.GetFiles(fileExtension, SearchOption.AllDirectories);
// set the size of the progress bar
progress.ProgressbarLength = jpgList.Count();
// Now we go through all our results and save them to our backup folder.
foreach (FileInfo tmp in jpgList)
{
string sourceFilePath = tmp.FullName;
string destFilePath = PATHTOBACKUP + "\\" + tmp.Name;
progress.IsThreadRunning = true;
try
{
System.IO.File.Copy(sourceFilePath, destFilePath, true);
}
catch (IOException ioe)
{
MessageBox.Show(ioe.Message);
}
}
}
}
// progress.IsThreadRunning = false;
}
catch (UnauthorizedAccessException e)
{
MessageBox.Show(e.Message);
}
}
It's pretty obvious that I have to do this after this function
System.IO.File.Copy(sourceFilePath, destFilePath, true);
But how do I report this to my ProgressWindow?
I really hope I explained it well enough, not sure if I'm missing something important.
Thanks in advance guys
Here is a compact example of the key components:
Clicking button starts new thread worker
Progress is done by file lengths, not by number of files
BeginInvoke used to update the progress bar (avoid cross Thread exception)
ProgressBar pb = new ProgressBar() { Minimum = 0, Maximum = 100 };
Button btn = new Button();
btn.Click += delegate {
Thread t = new Thread(() => {
DirectoryInfo dir = new DirectoryInfo("C:\\temp\\");
var files = dir.GetFiles("*.txt");
long totalLength = files.Sum(f => f.Length);
long length = 0;
foreach (var f in files) {
length += f.Length;
int percent = (int) Math.Round(100.0 * length / totalLength);
pb.BeginInvoke((Action) delegate {
pb.Value = percent;
});
File.Copy(f.FullName, "...");
}
});
t.IsBackground = true;
t.Start();
};
I have the following code, I'm trying to open a directory and process the files in it via the Background worker but I am having issues with it.
The error I have is (The name filePath does not exist in the current context), which I can understand because it's stored in another method? if someone could point out to me what is wrong with my code it would be appreciated. Folderbrowser doesn't work under the Background worker section.
private void btnFiles_Click(object sender, EventArgs e)
{
//btnFiles.Enabled = false;
btnSTOP.Enabled = true;
//Clear text fields
listBoxResults.Items.Clear();
listBoxPath.Items.Clear();
txtItemsFound.Text = String.Empty;
//Open folder browser for user to select the folder to scan
DialogResult result = folderBrowserDialog1.ShowDialog();
if (result == DialogResult.OK)
{
//Store selected folder path
string filePath = folderBrowserDialog1.SelectedPath;
}
//Start the async operation here
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//Process the folder
try
{
foreach (string dir in Alphaleonis.Win32.Filesystem.Directory.EnumerateFiles(filePath, "*.*", SearchOption.AllDirectories, true))
{
//Populate List Box with all files found
this.Invoke(new Action(() => listUpdate2(dir)));
FileInfo fi = new FileInfo(dir);
if (fi.Length == 0)
{
//Populate List Box with all empty files found
this.Invoke(new Action(() => listUpdate1(dir + Environment.NewLine)));
}
}
}
//Catch exceptions
catch (Exception err)
{
// This code just writes out the message and continues to recurse.
log.Add(err.Message);
//throw;
}
finally
{
//add a count of the empty files here
txtItemsFound.Text = listBoxResults.Items.Count.ToString();
// Write out all the files that could not be processed.
foreach (string s in log)
{
this.Invoke(new Action(() => listUpdate1(s)));
}
log.Clear();
MessageBox.Show("Scanning Complete", "Done", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
//If cancel button was pressed while the execution is in progress
//Change the state from cancellation ---> cancelled
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
//backgroundWorker1.ReportProgress(0);
return;
}
//}
//Report 100% completion on operation completed
backgroundWorker1.ReportProgress(100);
}
#DonBoitnott solution is the most general for data flow inside class. Specifically to BackgroundWorker there is another one exists
private void btnFiles_Click(object sender, EventArgs e)
{
...
// pass folder name
backgroundWorker1.RunWorkerAsync(folderBrowserDialog1.SelectedPath);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// get passed folder name
string filePath = (string)e.Argument;
...
}
The variable "filePath" is being declared local to the btnFiles_Click method. In order for it to be used elsewhere, it must be declared global to the code page:
public class Form1
{
private String _filePath = null;
private void btnFiles_Click(object sender, EventArgs e)
{
//get your file and assign _filePath here...
_filePath = folderBrowserDialog1.SelectedPath;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//use _filePath here...
}
}
When the code is working so laggy it would be very good so that it is not laggy when it works.
How the code works:
It searches the computer for a file is then when it find it to change the file but if the file is running line will loop until it manages to do its job.
Main class
public Form1(string[] Args)
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000); // One second.Thread.Sleep(1000); // One second.
MessageBox.Show("Testing");
Fille mc = new Fille();
mc.Search();
}
Fille clss
private static ArrayList list2 = new ArrayList();
private static ArrayList listRemove = new ArrayList();
public void Search()
{
try
{
foreach (string file in Directory.EnumerateFiles(#"C:\Users\user\Downloads\MCFILE\trrtrt\", "*.exe", SearchOption.AllDirectories))
{
// Display file path.
if (SHA1Hash.GetSHA1Hash(file) == "1233456") // fake SHA1Hash
{
try
{
COPYWithReplace(#"C:\Users\user\Downloads\MCFILE\Fake2\Test.exe", file);
}
catch (IOException)
{
// log errors
if (list2.Count == 0)
{
list2.Add(file);
Thread thread = new Thread(new ThreadStart(Test2));
thread.Start();
}
else
{
Thread thread = new Thread(new ThreadStart(Test2));
thread.Abort();
list2.Add(file);
thread.Join();
}
}
}
}
}
catch (Exception ex)
{
// log errors
}
}
private void Test2()
{
if (list2.Count == 0)
{
}
else
{
foreach (string _item in list2)
{
try
{
//Run
COPYWithReplace(#"C:\Users\user\Downloads\MCFILE\Fake2\Test.exe", _item);
listRemove.Add(_item);
}
catch (IOException)
{
//error
}
}
foreach (var Remove in listRemove)
{
list2.Remove(Remove);
}
listRemove.Clear();
if (list2.Count == 0)
{
}
else
{
Thread thread = new Thread(new ThreadStart(Test2));
thread.Start();
}
}
}
I made a new thread because I found the problem. But now it's just that it lags.
I suspect the reason it's "lagging" is because you have the system in a very convoluted but rather processor intensive and I/O intensive loop. If a file fails the first test, your code starts a thread that tries it again. And if that fails then you start another thread to try it again, lather, rinse, repeat.
That's going to absolutely kill performance. You're basically doing this:
while (forever)
{
if I can overwrite the file
{
break;
}
}
Except if you have multiple files that you're trying to write, then you're doing that loop for every file. Concurrently. And you're not just using a loop. Instead, you're starting and stopping threads like nobody's business.
Yeah, that's going to slow down your computer.
A more reasonable way to do this would be with a thread to do the first check, and a timer that will limit how often you do the other checks. Communication is with a simple queue, because only one thread will be accessing it at a time.
Here's what I would recommend:
private static Queue<string> filesToCheck = new Queue<string>();
private System.Timers.Timer copyTimer;
public void Search()
{
try
{
foreach (string file in Directory.EnumerateFiles(#"C:\Users\user\Downloads\MCFILE\trrtrt\", "*.exe", SearchOption.AllDirectories))
{
// Display file path.
if (SHA1Hash.GetSHA1Hash(file) == "1233456") // fake SHA1Hash
{
if (!TryToCopy(file)) // See function below
{
filesToCheck.Enqueue(file);
}
}
}
// Checked all the files once.
// If there are any in the queue, start the timer.
if (filesToCheck.Count > 0)
{
copyTimer = new System.Timers.Timer(CopyTimerProc, null, 1000, Timeout.Infinite);
}
}
catch (Exception)
{
// do your error handling
}
}
private void CopyTimerProc(object state)
{
string filename = filesToCheck.Dequeue();
if (TryToCopy(filename))
{
// success. If the queue is empty, kill the timer.
if (filesToCheck.Count == 0)
{
copyTimer.Dispose();
}
}
else
{
// File still locked.
// Put it back on the queue and reset the timer.
filesToCheck.Enqueue(filename);
copyTimer.Change(1000, 0);
}
}
private bool TryToCopy(string filename)
{
try
{
COPYWithReplace(#"C:\Users\user\Downloads\MCFILE\Fake2\Test.exe", filename);
return true;
}
catch (IOException)
{
// log error
return false;
}
}
The timer is a one-shot that is reset after each time it ticks. The reason I did it this way is to prevent another tick coming along while the previous tick is still processing. After all, it takes time to copy a file.
There's no reason to do this with a bunch of threads. The file system can only do one thing at a time, anyway, and it's not like an extra second or two while you wait for a file to become available is going to hurt anything.
I need to modify the GUI from inside of a method that takes long time to finish. As I read other posts,
one of the solution is to use Control.Dispatcher.BeginInvoke to set the GUI inside the worker thread.
However, I don't have a clue how to do this here.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew( () =>
{
ProcessFilesThree();
});
}
private void ProcessFilesThree()
{
string[] files = Directory.GetFiles(#"C:\temp\In", "*.jpg", SearchOption.AllDirectories);
Parallel.ForEach(files, (currentFile) =>
{
string filename = Path.GetFileName(currentFile);
// the following assignment is illegal
this.Text = string.Format("Processing {0} on thread {1}", filename,
Thread.CurrentThread.ManagedThreadId);
});
this.Text = "All done!"; // <- this assignment is illegal
}
}
Try the following:
msg = string.Format("Processing {0} on thread {1}", filename,
Thread.CurrentThread.ManagedThreadId);
this.BeginInvoke( (Action) delegate ()
{
this.Text = msg;
});
private void ProcessFilesThree()
{
// Assuming you have a textbox control named testTestBox
// and you wanted to update it on each loop iteration
// with someMessage
string someMessage = String.Empty;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000); //Simulate a second of work.
someMessage = String.Format("On loop iteration {0}", i);
testTextBox.Dispatcher.BeginInvoke(new Action<string>((message) =>
{
testTextBox.Text = message;
}), someMessage);
}
}