I have an application which i am add files to my Listbox and run those files.
My application play this files using PcapDot.Net project DLLs and send the packets through the network adapter.
The way is very simple: after all the files added to my application Listbox and the play button clicked the application handle the first file and after this file finished the next file began.
what i want to do is add control to my GUI that control the number of open thread in order to have the ability to play several file simultaneous.
This is my play button event:
private BackgroundWorker bw;
private void btnPlay_Click(object sender, EventArgs e)
{
bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
manualResetEvent = new ManualResetEvent(false);
if (bw.IsBusy != true)
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < listBoxFiles.Items.Counti++) //run in loop all over my listbox
{
// here if have several wiresharkFile events that update my UI:
wiresharkFile.statusChangedEvent += new WiresharkFile.StatusChangedDelegate(
(wFile) =>
{
bw.ReportProgress(wiresharkFile.packetProgressPrecentage, wiresharkFile);
});
wiresharkFile.sendBuffer(); //play the file
}
}
What is the best way to add option to open more than 1 thread in the same time ?
here is a simple example for your use, it shows how to create and sign to an event you'll pop when the thread that open a file ends and then you can, when the event pop, to open another file. make sure you keep a counter and a lock so you won't have race conditions
public delegate void FileClosedHndlr();
public class MyThread
{
private event FileClosedHndlr FileClosed;
public void MyMain()
{
Thread t = new Thread(new ThreadStart(start));
FileClosed += new FileClosedHndlr(MyThread_FileClosed);
t.Start();
}
void MyThread_FileClosed()
{
// Thread has ended file open
// open another file
}
private void start()
{
// Open the file
// End thread
if (FileClosed != null)
{
FileClosed();
}
}
}
it took me a while, so use it
Related
I have a UserControl with a big table that is displaying values using a lot of converters. I am trying to display a ProgressBar in a new Window with Indeterminate State that is closing automatically when the UserControl Loaded event is fired.
This is the Thread creation in the backcode of my UserControl :
Thread progressBarThread = new Thread(new ThreadStart(delegate {
ProgressBarWindow progressBarWindow = new ProgressBarWindow();
progressBarWindow.IsIndeterminate = true;
progressBarWindow.LaunchProgressBarInBackground();
progressBarWindow.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => { Dispatcher.FromThread(progressBarThread).InvokeShutdown(); };
This code is "working", it is opening progressBarWindow but when I shutdown the thread using InvokeShutdown (the ugliest way to do it, i agree). The problem is that the DoWork from my backgroundWorker.
Here is the DoWork function :
private void BackgroundWorker_WaitUntilShouldStop(object sender, DoWorkEventArgs e)
{
// Do not access the form's BackgroundWorker reference directly.
// Instead, use the reference provided by the sender parameter.
BackgroundWorker bw = sender as BackgroundWorker;
// Start the time-consuming operation.
while (!bw.CancellationPending)
{
Thread.Sleep(500);
}
}
I would like to call my function contained in ProgressBarWindow to stop the DoWork from runnning and close the ProgressBarWindow normaly using :
progressBar.StopProgressBarInBackground();
This method is calling backgroundWorker.CancelAsync();
This will result in backgroundWorker terminating and progressBarWindow closing automatically.
But I don't have access to progressBar that is inside the progressBarThread. I tried to pass my UserControl using :
progressBarThread.Start(this);
this being the main window.
When trying to pass a variable from the main thread, this error is thrown :
An exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll but was not handled in user code Additional information: The calling thread cannot access this object because a different thread owns it.
Does someone have a nice and correct way to do it without using myThread.InvokeShutdown() ?
Edit 1 :
I found a solution to my problem using a volatile variable :
volatile bool _isLoaded;
void CreateAndStopProgressBarWhenIsLoaded()
{
Thread progressBarThread= new Thread(new ThreadStart(
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += (sender, e) => {
if (_isLoaded)
progressBar.StopProgressBarInBackground();
};
// Try to stop `progressBar` every 500 ms
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(500);
dispatcherTimer.Start();
progressBar.ShowDialog();
// Will only be reached once progressBar.ShowDialog(); returns
dispatcherTimer.Stop();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
_isLoaded = this.IsLoaded;
progressBarThread.Join(); // Wait for progressBarThread to end
};
}
Now the question is do you have a better solution ?
Edit 2 :
Here is my final solution thanks to #AlexSeleznyov :
void CreateAndStopProgressBarWhenIsLoaded()
{
Controls.ProgressBar.ProgressBar pb = null;
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
Thread progressBarThread = new Thread(new ThreadStart(delegate
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
pb = progressBar;
manualResetEvent.Set();
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
progressBar.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
pb.Dispatcher.Invoke(delegate {
manualResetEvent.WaitOne();
pb.StopProgressBarInBackground();
});
progressBarThread.Join();
};
}
You might try this approach, to cache ProgressBar instance and then use it from another thread. Dispatcher.Invoke eradicates need for CheckAccess I've mentioned in comments.
void CreateAndStopProgressBarWhenIsLoaded()
{
var pb = new Controls.ProgressBar.ProgressBar[1];
Thread progressBarThread= new Thread(new ThreadStart(
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
pb[0] = progressBar;
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
progressBar.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
pb[0].Dispatcher.Invoke(()=>pb[0].Close());
};
}
I think that you can use BackgroundWorker.RunWorkerCompleted in ProgressBarWindow - it will be invoked when you cancel a backgroundWorker.
private void backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
//close the window
}
}
I'm facing an serious issue where data from one record is copied to another record (Overlay). I'm using MQ request for communicating to Mainframe systems using my C# code. we are facing issue which is very random/rarer where sending update request to Mainframe for one record copy information of another record previously processed by that thread. I'm using below code Background worker approach to create multi-threading on my servers.
My Question here is : Can objects created by one worker being used by another work ? is that possible ? this may be one of reason of overlay data.
Please help with you suggestion !!
BackgroundWorker worker;
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
worker.WorkerReportsProgress = true;
worker.RunWorkerAsync();
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
struct SOmeData { }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
SOmeData data = new SOmeData();
// backgroundWorker1 result
e.Result = data;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// backgroundWorker1 result
SOmeData data = (SOmeData)e.Result;
// start backgroundWorker2
backgroundWorker2.RunWorkerAsync(data);
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
// backgroundWorker1 result
SOmeData data = (SOmeData)e.Argument;
}
}
In my application i am checking my files by open Wireshark process before add to my Listbox.
this is Add Directory click event who take root folder and checking all this files inside this folder and sub folders:
private void btnAddDir_Click(object sender, EventArgs e)
{
try
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
ThreadStart threadStart = delegate
{
foreach (string file in SafeFileEnumerator.EnumerateFiles(folderBrowserDialog1.SelectedPath, "*.*", SearchOption.AllDirectories))
{
Interlocked.Increment(ref numWorkers);
StartBackgroundFileChecker(file);
}
};
Thread thread = new Thread(threadStart);
thread.IsBackground = true;
thread.Start();
}
}
catch (Exception)
{ }
}
private void StartBackgroundFileChecker(string file)
{
ListboxFile listboxFile = new ListboxFile();
listboxFile.OnFileAddEvent += listboxFile_OnFileAddEvent;
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork +=
(s3, e3) =>
{
//check my file
};
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Interlocked.Decrement(ref numWorkers) == 0)
{
//update my UI
}
}
When i am checking this file i am open Wireshark process so if i choose folder with many files, many Wireshark processes opned and this take a lot on memory,
how can i wait until my BackgroundWorker finish and only then open new one ?
As I understand you want only single background worker launched at time. If so, then try this (based on System.Threading.AutoResetEvent)
//introduce additional field
private AutoResetEvent _workerCompleted = new AutoResetEvent(false);
//modify StartBackgroundFileChecker
private void StartBackgroundFileChecker(string file)
{
ListboxFile listboxFile = new ListboxFile();
listboxFile.OnFileAddEvent += listboxFile_OnFileAddEvent;
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork +=
(s3, e3) =>
{
//check my file
};
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
//new code - wait for completion
_workerCompleted.WaitOne();
}
//add completion notification to backgroundWorker_RunWorkerCompleted
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Interlocked.Decrement(ref numWorkers) == 0)
{
//update my UI
}
//new code - notify about completion
_workerCompleted.Set();
}
In that solution your background thread will start new BackgroundWorker one by one - this can be not optimal (you could avoid BackgroundWorker at all and simply update UI via Dispatch in threadStart delegate)
In my opinion better to control number of parallel threads and still process files in multiple but limited numbers of threads.
Here is the alternative solution (based on System.Threading.Tasks namespace):
private void btnAddDir_Click(object sender, EventArgs e)
{
var selectedPath = folderBrowserDialog1.SelectedPath;
Task.Factory.StartNew(() =>
{
var files = Directory.EnumerateFiles(selectedPath, "*.*", SearchOption.AllDirectories);
Parallel.ForEach(files,
new ParallelOptions
{
MaxDegreeOfParallelism = 10 // limit number of parallel threads here
},
file =>
{
//process file here - launch your process
});
}).ContinueWith(
t => { /* when all files processed. Update your UI here */ }
,TaskScheduler.FromCurrentSynchronizationContext() // to ContinueWith (update UI) from UI thread
);
}
You can tweak this solution for your specific needs.
Used classes/methods (see MSDN for reference):
Task
TaskScheduler.FromCurrentSynchronizationContext
Parallel.ForEach Method (IEnumerable, ParallelOptions, Action)
Maybe something like this, instead of the foreach keep a List of the files and after complete just take the first element and update your List
private List<string> _files;
private void btnAddDir_Click(object sender, EventArgs e)
{
try
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
_files = new List<string>(SafeFileEnumerator.EnumerateFiles(folderBrowserDialog1.SelectedPath, "*.*", SearchOption.AllDirectories));
Interlocked.Increment(ref numWorkers);
var file = _files.FirstOrDefault();
if(file != null)
StartBackgroundFileChecker(file);
}
}
catch (Exception)
{ }
}
private void StartBackgroundFileChecker(string file)
{
ListboxFile listboxFile = new ListboxFile();
listboxFile.OnFileAddEvent += listboxFile_OnFileAddEvent;
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork +=
(s3, e3) =>
{
//check my file
};
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Interlocked.Decrement(ref numWorkers) == 0)
{
//update my UI
_files = _files.Skip(1);
var file = _files.FirstOrDefault();
if(file != null)
StartBackgroundFileChecker(file);
}
}
I have the following code:
StatusLabel.Content = "Copying files...";
AutoCopy();
StatusLabel.Content = "Finished";
The above code is a button click and when I click the button, I expect to see a label with "Copying files...", then it will copying files via an AutoCopy method and then the label with "Finished"
I do not see "Copying files". All I see is the screen freeze up and then unfreezed with "Finished".
How can I get "Copying files to show..." and only after AutoCopy() is finished, do I want to show "Finished";
As everyone said, your UI (main) thread is blocked during file copy operation.
You need to spin off a worker thread that does everything in the background.
Caution: Multithreading only adds complexity.
{
...
System.ComponentModel.BackgroundWorker bw = new System.ComponentModel.BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
StatusLabel.Content = "Copying files...";
bw.RunWorkerAsync();
...
}
private void DoWork(object sender, DoWorkEventArgs e)
{
AutoCopy();
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
StatusLabel.Content = "Finished";
}
This case is using C# WPF. I want to instantly disable a button after clicking it to prevent clicking it twice in short succession. I disabled the button in OnClick_Event but still clickable.
Part of source is as below.
private void Button_Click_UpdateBurndownChart(object sender, RoutedEventArgs e)
{
if(threadNotWorking)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
private void startWorkThread()
{
... ...
//after finish required process
updateButton.IsEnabled = true;
}
Is there any way to accomplish this?
you may want to use a dispatcher, there is probably a threading problem (callback function running on seperate thread and trying to access ui which runs on another thread). try this . .
updateButton.Dispatcher.BeginInvoke(
new ThreadStart(() => updateButton.IsEnabled = false),
System.Windows.Threading.DispatcherPriority.Input, null);
instead of
updateButton.IsEnabled = false;
What happens if you were instead to change the order of your events from:
updateButton.IsEnabled = false;
startWorkThread();
To
startWorkThread();
updateButton.IsEnabled = false;
Let me know how this goes.
What it looks like is that you are starting your thread then immediatly enabling your button before your thread has finished. You would be better off using a BackgroundWorker and enable your Button in the RunWorkerCompleted Event. Though you can do something similar by enabling your button using a BeginInvoke at the end of your Process.
public void doWork()
{
System.Threading.Thread.Sleep(10000); //Simulating your Process
Dispatcher.BeginInvoke(new System.Threading.ThreadStart(delegate() { updateButton.IsEnabled = true; }), System.Windows.Threading.DispatcherPriority.Background);
}
Example with BackgroundWorker
using System.ComponentModel;
public partial class MainWindow : Window
{
BackgroundWorker bgw;
public MainWindow()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
updateButton.IsEnabled = true;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(10000); //Simulating your work
}
private void startWorkThread()
{
bgw.RunWorkerAsync();
}
private void updateButton_Click(object sender, RoutedEventArgs e)
{
if (bgw.IsBusy != true)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
}