how to use text based console progress bar in multithreading C# - c#

Hi I have a program that downloads + extracts the files in parallel (in threads).
it is a console app,and I want to show the Progress Bar for each operation in each thread.
for eg:
File 1 [==========35% ] 35mb of 100mb downloaded
File 2 [====20% ] 20mb of 100mb downloaded
File1 Downloaded,
File 1 [=============50% ] 50% extracted.
and so on.
note: I am able to show the console outputs as Code below, but would like to use this Progress Bar in my Console APP.
How can I use solution proposed in https://gist.github.com/DanielSWolf/0ab6a96899cc5377bf54 in this case ?
public static void DownloadAndGetFiles()
{
try
{
Parallel.ForEach(FileIds, currentId =>
{
int currentId = FileIds.Id
clientFileDownload(currentId);
});
}
catch (Exception e)
{
}
}
private static void clientFileDownload(int currentId)
{
WebClient client = new WebClient();
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
string downloadedFile = #"d:\tmp\";
client.DownloadFileAsync(new Uri(currentId.URL), downloadedFile); //some URL
while (client.IsBusy) { }
string temp = ExtractAndRename(currentId);
}
private static void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
//Prints: "Downloaded 3mb of 61.46mb (4%)"
Console.WriteLine("Downloaded "
+ ((e.BytesReceived / 1024f) / 1024f).ToString("#0.##") + "mb"
+ " of "
+ ((e.TotalBytesToReceive / 1024f) / 1024f).ToString("#0.##") + "mb"
+ " (" + e.ProgressPercentage + "%)");
}
private static string ExtractAndRename(int currentId)
{
//using SevenZipExtractor lib http://stackoverflow.com/questions/20898794/how-to-extract-files-in-my-archive-one-by-one-using-sevenzipsharp
SevenZipExtractor extractor = new SevenZipExtractor(#"d:\tmp\" + id.Name);
extractor.Extracting += extractor_Extracting;
extractor.ExtractArchive(#"d:\tmp\" + extractName[0]);
return (#"d:\tmp\" + extractName[0]);
}
public static void extractor_Extracting(object sender, SevenZip.ProgressEventArgs p)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("\b\b{0}% Extracted", p.PercentDone);
Console.ResetColor();
}

Provide every thread with a variable y which contains the line number it is allowed to write to.
Before a thread wants to update the screen, create a lock. The console can be used by only one thread at a time. Otherwise results of several threads will mix up.
Move the cursor to line specified by y and update that line.
Release the lock.
An example:
static private readonly object _sync = new object();
private static void UpdateProgress(int y, string item, int progress, int total)
{
int percentage = (int)100.0 * progress / total;
lock(_sync)
{
Console.CursorLeft = 0;
Console.CursorTop = y;
Console.Write(item + " [" + new string('=', percentage / 2) + "] " + percentage + "%");
}
}
You can call this method from your method clientFileDownload, which has to be modified a bit:
private static void clientFileDownload(int currentId, int y)
and should be called when creating the threads like this:
int y = 0;
Parallel.ForEach(FileIds, currentId =>
{
int currentId = FileIds.Id
clientFileDownload(currentId, y);
Interlocked.Increment(ref y);
});

Related

C# Wait until data received and go to next iteration

I'm using Serial Port to get data from device in for loop iteration.
The problem is in loop iteration i need to get data from serial port, validate it and going to the next iteration.
How i can achieved this?
Here are my code :
private void processData()
{
// Loop Procedure
int x = Int32.Parse(master["Cycle"].ToString());
int y = Int32.Parse(master["MinWeight"].ToString());
// Loop for each line
for (int i = this.CLine; i < 2; i++)
{
this.CLine = i;
if (i == 0)
label15.Text = master["LLINE"].ToString();
else
label15.Text = master["RLINE"].ToString();
IDictionary<string, string> dic = (Dictionary<String, String>)master[i.ToString()];
label18.Text = this.CProcess = dic["PROCESSID"];
int z = Int32.Parse(dic["PRODLANE"].ToString());
// Loop for each sampling session (Cycle)
for (int j = this.CCycle; j <= x; j++)
{
this.CCycle = j;
// Loop for production lane
for (int k = this.CLane; k <= z; k++)
{
this.CLane = k;
label16.Text = k.ToString();
// In this section i want to send command over serial port
// get value from my device
// validate it if current weight bellow standard weight
// do it again (get data from device)
// else we can go to next iteration
while (this.CWeight < y)
{
XApi.l("xxx2 " + this.CWeight + " vs " + y + " " + k.ToString() + " " + this.isDataReady);
SendData("Q");
}
// Commit Transaction
// XDb.CommitTrans(this.CCycle.ToString(), dic["LINEID"].ToString(), this.CLane.ToString(), weight.ToString(), this.isTrialStage == true ? "1" : "0");
}
}
}
}
I've tried this
while (this.CWeight < y)
{
XApi.l("xxx2 " + this.CWeight + " vs " + y + " " + k.ToString() + " " + this.isDataReady);
SendData("Q");
}
but it seems blocked UI thread and make my application solaggy.
Anyone can give me some idea? Thanks in advance.
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (e.EventType == System.IO.Ports.SerialData.Eof)
return;
// If the com port has been closed, do nothing
if (!comport.IsOpen)
return;
// Update flag data received
this.isDataReady = true;
// Determain which mode (string or binary) the user is in
if (CurrentDataMode == DataMode.Text)
{
// Read all the data waiting in the buffer
string data = comport.ReadExisting();
// Update result
result += data;
if (result.Length > 16)
{
SetText(result.ToString());
}
// Display the text to the user in the terminal
Log(LogMsgType.Incoming, data);
}
else
{
// Obtain the number of bytes waiting in the port's buffer
int bytes = comport.BytesToRead;
// Create a byte array buffer to hold the incoming data
byte[] buffer = new byte[bytes];
// Read the data from the port and store it in our buffer
comport.Read(buffer, 0, bytes);
// Show the user the incoming data in hex format
Log(LogMsgType.Incoming, ByteArrayToHexString(buffer));
}
}
private void SendData(String msg)
{
this.isDataReady = false;
result = "";
if (CurrentDataMode == DataMode.Text)
{
// Send the user's text straight out the port
comport.Write(msg + "\r\n");
// Show in the terminal window the user's text
Log(LogMsgType.Outgoing, msg + "\n");
}
else
{
try
{
// Convert the user's string of hex digits (ex: B4 CA E2) to a byte array
byte[] data = HexStringToByteArray(txtSendData.Text);
// Send the binary data out the port
comport.Write(data, 0, data.Length);
// Show the hex digits on in the terminal window
Log(LogMsgType.Outgoing, ByteArrayToHexString(data) + "\n");
}
catch (FormatException)
{
// Inform the user if the hex string was not properly formatted
Log(LogMsgType.Error, "Not properly formatted hex string: " + txtSendData.Text + "\n");
}
}
}
Anyone can give me some idea?
You can use async/await in your code not to block your UI by writing an extension method like below. Usage would be:
async void SomeMethod()
{
SerialPort serialPort = .......
while (true)
{
serialPort.Write(.....);
var retval = await serialPort.ReadAsync();
}
}
The keyword here is using TaskCompletionSource class with your events...
static public class SerialPortExtensions
{
public static Task<byte[]> ReadAsync(this SerialPort serialPort)
{
var tcs = new TaskCompletionSource<byte[]>();
SerialDataReceivedEventHandler dataReceived = null;
dataReceived = (s, e) =>
{
serialPort.DataReceived -= dataReceived;
var buf = new byte[serialPort.BytesToRead];
serialPort.Read(buf, 0, buf.Length);
tcs.TrySetResult(buf);
};
serialPort.DataReceived += dataReceived;
return tcs.Task;
}
}

Thread synchronization printing strings

I wrote a small programm which prints "x", then "+", then again "x" and so on.
The idea was to make it run in two threads so that the first thread prints "x" and the second prints "+". The output looks like this:
"x" -> Thread number 1
"+" -> Thread number 2
"x" -> Thread number 1enter code here
"+" -> Thread number 2
and so on..
What I wrote seems to work fine but it seems to me it is written in
very old-fashioned way:
public class Example
{
private static int count = 10;
private static int i = 0;
private static bool isOneActive = false;
private static void Run1(object o)
{
string s = o as string;
while(true)
{
if (!isOneActive)
{
Console.WriteLine("Hello from thread number: " +
Thread.CurrentThread.ManagedThreadId + " -> " + s);
isOneActive = true;
if (i++ > count) break;
}
}
}
private static void Run2(object o)
{
string s = o as string;
while(true)
{
if (isOneActive)
{
Console.WriteLine("Hello from thread number: " +
Thread.CurrentThread.ManagedThreadId + " -> " + s);
isOneActive = false;
if (i++ > count) break;
}
}
}
static void Main()
{
Thread t1 = new Thread(Run1);
Thread t2 = new Thread(Run2);
t1.Start("x");
t2.Start("+");
}
I know that now .NET has a lot of instruments for thread synchronization as for example ManualResetEvent class and Task library. So how could we write the same programm using ManualResetEvent class? Is it possible at all?
Your code isn't only old fashioned, it is very inefficient. It spins for no reason doing nothing but waiting; this is called Busy wait should be avoided whenever possible.
Better approach is to use Waithandles as noted in comments.
A naive implementation with very little change in your code will look something like the following.
public class Example
{
private static int count = 10;
private static int i = 0;
private static AutoResetEvent firstEvent = new AutoResetEvent(true);
private static AutoResetEvent secondEvent = new AutoResetEvent(false);
private static void Run1(object o)
{
string s = o as string;
while (true)
{
firstEvent.WaitOne();
Console.WriteLine("Hello from thread number: " + Thread.CurrentThread.ManagedThreadId + " -> " + s);
secondEvent.Set();
if (Interlocked.Increment(ref i) > count)
break;
}
}
private static void Run2(object o)
{
string s = o as string;
while (true)
{
secondEvent.WaitOne();
Console.WriteLine("Hello from thread number: " + Thread.CurrentThread.ManagedThreadId + " -> " + s);
firstEvent.Set();
if (Interlocked.Increment(ref i) > count)
break;
}
}
static void Main()
{
Thread t1 = new Thread(Run1);
Thread t2 = new Thread(Run2);
t1.Start("x");
t2.Start("+");
}
}
Note that firstEvent is instantiated with the initialState flag set to true which means that first thread doesn't waits initially.
Consider this example (fiddle):
static void Main(string[] args)
{
var console = new object();
int i = 0;
Task.Run(() =>
{
lock (console)
while (i++ < 10)
{
Console.Write(i);
Monitor.Pulse(console);
Monitor.Wait(console);
}
});
Task.Run(() =>
{
lock (console)
while (i < 10)
{
Console.Write('+');
Monitor.Pulse(console);
Monitor.Wait(console);
}
});
Console.ReadLine(); // Task.WaitAll might be better, remove for fiddle
}

C# my rich textbox keeps adding all the song names instead of just 1 at the time

i'm making a little music program for school, and i keep having problems wiht my richtextbox (rtb), it keeps adding all the songs from before and i don't know how to fix it without cleaning it over and over again everytime i click the button.
private void AddSongKtab()
{
SongKtab[nr] = new SongInfo();
string m_SongName;
string m_SongLocation;
ofdOpenKtab.ShowDialog();
m_SongName = ofdOpenKtab.SafeFileName;
m_SongLocation = ofdOpenKtab.FileName;
SongKtab[nr].SongName = m_SongName;
SongKtab[nr].SongLocation = m_SongLocation;
tbAddKtab.Text = "";
tbAddKtab.Text = m_SongName;
rtbExtraKtab.AppendText("added" + m_SongName + "\n");
rtbExtraKtab.AppendText("Located at " + m_SongLocation + "\n\n");
rtbSongsKtab.ScrollToCaret();
nr++;
showAllKtab();
}
private void showAllKtab()
{
int m_index;
rtbExtraKtab.Clear();
for (m_index = 0; m_index < nr; m_index++)
{
rtbSongsKtab.AppendText(SongKtab[m_index].SongName + "\n");
}
}

Progress bar in console application

I'm writing a simple c# console app that uploads files to sftp server. However, the amount of files are large. I would like to display either percentage of files uploaded or just the number of files upload already from the total number of files to be upload.
First, I get all the files and the total number of files.
string[] filePath = Directory.GetFiles(path, "*");
totalCount = filePath.Length;
Then I loop through the file and upload them one by one in foreach loop.
foreach(string file in filePath)
{
string FileName = Path.GetFileName(file);
//copy the files
oSftp.Put(LocalDirectory + "/" + FileName, _ftpDirectory + "/" + FileName);
//Console.WriteLine("Uploading file..." + FileName);
drawTextProgressBar(0, totalCount);
}
In the foreach loop I have a progress bar which I have issues with. It doesn't display properly.
private static void drawTextProgressBar(int progress, int total)
{
//draw empty progress bar
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = 32;
Console.Write("]"); //end
Console.CursorLeft = 1;
float onechunk = 30.0f / total;
//draw filled part
int position = 1;
for (int i = 0; i < onechunk * progress; i++)
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw unfilled part
for (int i = position; i <= 31 ; i++)
{
Console.BackgroundColor = ConsoleColor.Green;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw totals
Console.CursorLeft = 35;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(progress.ToString() + " of " + total.ToString() + " "); //blanks at the end remove any excess
}
The output is just [ ] 0 out of 1943
What am I doing wrong here?
EDIT:
I'm trying to display the progress bar while I'm loading and exporting XML files. However, it's going through a loop. After it finishes the first round it goes to the second and so on.
string[] xmlFilePath = Directory.GetFiles(xmlFullpath, "*.xml");
Console.WriteLine("Loading XML files...");
foreach (string file in xmlFilePath)
{
for (int i = 0; i < xmlFilePath.Length; i++)
{
//ExportXml(file, styleSheet);
drawTextProgressBar(i, xmlCount);
count++;
}
}
It never leaves the for loop...Any suggestions?
I was also looking for a console progress bar. I didn't find one that did what I needed, so I decided to roll my own. Click here for the source code (MIT License).
Features:
Works with redirected output
If you redirect the output of a console application (e.g., Program.exe > myfile.txt), most implementations will crash with an exception. That's because Console.CursorLeft and Console.SetCursorPosition() don't support redirected output.
Implements IProgress<double>
This allows you to use the progress bar with async operations that report a progress in the range of [0..1].
Thread-safe
Fast
The Console class is notorious for its abysmal performance. Too many calls to it, and your application slows down. This class performs only 8 calls per second, no matter how often you report a progress update.
Use it like this:
Console.Write("Performing some task... ");
using (var progress = new ProgressBar()) {
for (int i = 0; i <= 100; i++) {
progress.Report((double) i / 100);
Thread.Sleep(20);
}
}
Console.WriteLine("Done.");
I know this is an old thread, and apologies for the self promotion, however I've recently written an open source console library available on nuget Goblinfactory.Konsole with threadsafe multiple progress bar support, that might help anyone new to this page needing one that doesnt block the main thread.
It's somewhat different to the answers above as it allows you to kick off the downloads and tasks in parallel and continue with other tasks;
cheers, hope this is helpful
A
var t1 = Task.Run(()=> {
var p = new ProgressBar("downloading music",10);
... do stuff
});
var t2 = Task.Run(()=> {
var p = new ProgressBar("downloading video",10);
... do stuff
});
var t3 = Task.Run(()=> {
var p = new ProgressBar("starting server",10);
... do stuff .. calling p.Refresh(n);
});
Task.WaitAll(new [] { t1,t2,t3 }, 20000);
Console.WriteLine("all done.");
gives you this type of output
The nuget package also includes utilities for writing to a windowed section of the console with full clipping and wrapping support, plus PrintAt and various other helpful classes.
I wrote the nuget package because I constantly ended up writing lots of common console routines whenever I wrote build and ops console scripts and utilities.
If I was downloading several files, I used to slowly Console.Write to the screen on each thread, and used to try various tricks to make reading the interleaved output on the screen easier to read, e.g. different colors or numbers. I eventually wrote the windowing library so that output from different threads could simply be printed to different windows, and it cut down a ton of boilerplate code in my utility scripts.
For example, this code,
var con = new Window(200,50);
con.WriteLine("starting client server demo");
var client = new Window(1, 4, 20, 20, ConsoleColor.Gray, ConsoleColor.DarkBlue, con);
var server = new Window(25, 4, 20, 20, con);
client.WriteLine("CLIENT");
client.WriteLine("------");
server.WriteLine("SERVER");
server.WriteLine("------");
client.WriteLine("<-- PUT some long text to show wrapping");
server.WriteLine(ConsoleColor.DarkYellow, "--> PUT some long text to show wrapping");
server.WriteLine(ConsoleColor.Red, "<-- 404|Not Found|some long text to show wrapping|");
client.WriteLine(ConsoleColor.Red, "--> 404|Not Found|some long text to show wrapping|");
con.WriteLine("starting names demo");
// let's open a window with a box around it by using Window.Open
var names = Window.Open(50, 4, 40, 10, "names");
TestData.MakeNames(40).OrderByDescending(n => n).ToList()
.ForEach(n => names.WriteLine(n));
con.WriteLine("starting numbers demo");
var numbers = Window.Open(50, 15, 40, 10, "numbers",
LineThickNess.Double,ConsoleColor.White,ConsoleColor.Blue);
Enumerable.Range(1,200).ToList()
.ForEach(i => numbers.WriteLine(i.ToString())); // shows scrolling
produces this
You can also create progress bars inside a window just as easily as writing to the windows. (mix and match).
You might want to try https://www.nuget.org/packages/ShellProgressBar/
I just stumbled upon this progress bar implementation - its cross platform, really easy to use, quite configurable and does what it should right out of the box.
Just sharing because i liked it a lot.
This line is your problem:
drawTextProgressBar(0, totalCount);
You're saying the progress is zero in every iteration, this should be incremented. Maybe use a for loop instead.
for (int i = 0; i < filePath.length; i++)
{
string FileName = Path.GetFileName(filePath[i]);
//copy the files
oSftp.Put(LocalDirectory + "/" + FileName, _ftpDirectory + "/" + FileName);
//Console.WriteLine("Uploading file..." + FileName);
drawTextProgressBar(i, totalCount);
}
I quite liked the original poster's progress bar, but found that it did not display progress correctly with certain progress/total item combinations. The following, for example, does not draw correctly, leaving an extra grey block at the end of the progress bar:
drawTextProgressBar(4114, 4114)
I re-did some of the drawing code to remove the unnecessary looping which fixed the above issue and also sped things up quite a bit:
public static void drawTextProgressBar(string stepDescription, int progress, int total)
{
int totalChunks = 30;
//draw empty progress bar
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = totalChunks + 1;
Console.Write("]"); //end
Console.CursorLeft = 1;
double pctComplete = Convert.ToDouble(progress) / total;
int numChunksComplete = Convert.ToInt16(totalChunks * pctComplete);
//draw completed chunks
Console.BackgroundColor = ConsoleColor.Green;
Console.Write("".PadRight(numChunksComplete));
//draw incomplete chunks
Console.BackgroundColor = ConsoleColor.Gray;
Console.Write("".PadRight(totalChunks - numChunksComplete));
//draw totals
Console.CursorLeft = totalChunks + 5;
Console.BackgroundColor = ConsoleColor.Black;
string output = progress.ToString() + " of " + total.ToString();
Console.Write(output.PadRight(15) + stepDescription); //pad the output so when changing from 3 to 4 digits we avoid text shifting
}
I have copy pasted your ProgressBar method. Because your error was in the loop as the accepted answer mentioned. But the ProgressBar method has some syntax errors too. Here is the working version. Slightly modified.
private static void ProgressBar(int progress, int tot)
{
//draw empty progress bar
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = 32;
Console.Write("]"); //end
Console.CursorLeft = 1;
float onechunk = 30.0f / tot;
//draw filled part
int position = 1;
for (int i = 0; i < onechunk * progress; i++)
{
Console.BackgroundColor = ConsoleColor.Green;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw unfilled part
for (int i = position; i <= 31; i++)
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw totals
Console.CursorLeft = 35;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(progress.ToString() + " of " + tot.ToString() + " "); //blanks at the end remove any excess
}
Please note that #Daniel-wolf has a better approach: https://stackoverflow.com/a/31193455/169714
I've created this handy class that works with System.Reactive. I hope you find it lovely enough.
public class ConsoleDisplayUpdater : IDisposable
{
private readonly IDisposable progressUpdater;
public ConsoleDisplayUpdater(IObservable<double> progress)
{
progressUpdater = progress.Subscribe(DisplayProgress);
}
public int Width { get; set; } = 50;
private void DisplayProgress(double progress)
{
if (double.IsNaN(progress))
{
return;
}
var progressBarLenght = progress * Width;
System.Console.CursorLeft = 0;
System.Console.Write("[");
var bar = new string(Enumerable.Range(1, (int) progressBarLenght).Select(_ => '=').ToArray());
System.Console.Write(bar);
var label = $#"{progress:P0}";
System.Console.CursorLeft = (Width -label.Length) / 2;
System.Console.Write(label);
System.Console.CursorLeft = Width;
System.Console.Write("]");
}
public void Dispose()
{
progressUpdater?.Dispose();
}
}
Console Progress Bar in C# (Complete Code - Just copy and paste on your IDE)
class ConsoleUtility
{
const char _block = '■';
const string _back = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
const string _twirl = "-\\|/";
public static void WriteProgressBar(int percent, bool update = false)
{
if (update)
Console.Write(_back);
Console.Write("[");
var p = (int)((percent / 10f) + .5f);
for (var i = 0; i < 10; ++i)
{
if (i >= p)
Console.Write(' ');
else
Console.Write(_block);
}
Console.Write("] {0,3:##0}%", percent);
}
public static void WriteProgress(int progress, bool update = false)
{
if (update)
Console.Write("\b");
Console.Write(_twirl[progress % _twirl.Length]);
}
}
//This is how to call in your main method
static void Main(string[] args)
{
ConsoleUtility.WriteProgressBar(0);
for (var i = 0; i <= 100; ++i)
{
ConsoleUtility.WriteProgressBar(i, true);
Thread.Sleep(50);
}
Console.WriteLine();
ConsoleUtility.WriteProgress(0);
for (var i = 0; i <= 100; ++i)
{
ConsoleUtility.WriteProgress(i, true);
Thread.Sleep(50);
}
}
I just stumbled upon this thread looking for something else, and I thought I'd drop off my code that I put together that downloads a List of files using the DownloadProgressChanged. I find this super helpful so I not only see the progress, but the actual size as the file is coming through. Hope it helps someone!
public static bool DownloadFile(List<string> files, string host, string username, string password, string savePath)
{
try
{
//setup FTP client
foreach (string f in files)
{
FILENAME = f.Split('\\').Last();
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
wc.DownloadFileAsync(new Uri(host + f), savePath + f);
while (wc.IsBusy)
System.Threading.Thread.Sleep(1000);
Console.Write(" COMPLETED!");
Console.WriteLine();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
return true;
}
private static void ProgressChanged(object obj, System.Net.DownloadProgressChangedEventArgs e)
{
Console.Write("\r --> Downloading " + FILENAME +": " + string.Format("{0:n0}", e.BytesReceived / 1000) + " kb");
}
private static void Completed(object obj, AsyncCompletedEventArgs e)
{
}
Here's an example of the output:
Hope it helps someone!
I am still a little new to C# but I believe the below might help.
string[] xmlFilePath = Directory.GetFiles(xmlFullpath, "*.xml");
Console.WriteLine("Loading XML files...");
int count = 0;
foreach (string file in xmlFilePath)
{
//ExportXml(file, styleSheet);
drawTextProgressBar(count, xmlCount);
count++;
}
Based on all posts above, I did an improved version.
No jumping cursors. It's invisible now.
Improved performance (costs 1/5~1/10 time of the origin one).
Interface based. Easy to move to something else.
public class ConsoleProgressBar : IProgressBar
{
private const ConsoleColor ForeColor = ConsoleColor.Green;
private const ConsoleColor BkColor = ConsoleColor.Gray;
private const int DefaultWidthOfBar = 32;
private const int TextMarginLeft = 3;
private readonly int _total;
private readonly int _widthOfBar;
public ConsoleProgressBar(int total, int widthOfBar = DefaultWidthOfBar)
{
_total = total;
_widthOfBar = widthOfBar;
}
private bool _intited;
public void Init()
{
_lastPosition = 0;
//Draw empty progress bar
Console.CursorVisible = false;
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = _widthOfBar;
Console.Write("]"); //end
Console.CursorLeft = 1;
//Draw background bar
for (var position = 1; position < _widthOfBar; position++) //Skip the first position which is "[".
{
Console.BackgroundColor = BkColor;
Console.CursorLeft = position;
Console.Write(" ");
}
}
public void ShowProgress(int currentCount)
{
if (!_intited)
{
Init();
_intited = true;
}
DrawTextProgressBar(currentCount);
}
private int _lastPosition;
public void DrawTextProgressBar(int currentCount)
{
//Draw current chunk.
var position = currentCount * _widthOfBar / _total;
if (position != _lastPosition)
{
_lastPosition = position;
Console.BackgroundColor = ForeColor;
Console.CursorLeft = position >= _widthOfBar ? _widthOfBar - 1 : position;
Console.Write(" ");
}
//Draw totals
Console.CursorLeft = _widthOfBar + TextMarginLeft;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(currentCount + " of " + _total + " "); //blanks at the end remove any excess
}
}
public interface IProgressBar
{
public void ShowProgress(int currentCount);
}
And some test code:
var total = 100;
IProgressBar progressBar = new ConsoleProgressBar(total);
for (var i = 0; i <= total; i++)
{
progressBar.ShowProgress(i);
Thread.Sleep(50);
}
Thread.Sleep(500);
Console.Clear();
total = 9999;
progressBar = new ConsoleProgressBar(total);
for (var i = 0; i <= total; i++)
{
progressBar.ShowProgress(i);
}

lock statement not working when there is a loop inside it?

See this code:
public class multiply
{
public Thread myThread;
public int Counter
{
get;
private set;
}
public string name
{
get;
private set;
}
public void RunConsolePrint()
{
lock(this)
{
RunLockCode("lock");
}
}
private void RunLockCode(string lockCode)
{
Console.WriteLine("Now thread "+lockCode+" " + name + " has started");
for (int i = 1; i <= Counter; i++)
{
Console.WriteLine(lockCode+" "+name + ": count has reached " + i + ": total count is " + Counter);
}
Console.WriteLine("Thread " + lockCode + " " + name + " has finished");
}
public multiply(string pname, int pCounter)
{
name = pname;
Counter = pCounter;
myThread = new Thread(new ThreadStart(RunConsolePrint));
}
}
And this is the test run code:
static void Main(string[] args)
{
int counter = 50;
multiply m2 = new multiply("Second", counter);
multiply m1 = new multiply("First", counter);
m1.myThread.Start();
m2.myThread.Start();
Console.ReadLine();
}
I would expect that m2 must execute from start to finish before m1 starts executing, or vice versa, because of the lock statement. But the result I found was the call to lock first and lock second was intermingled together, i.e., something like this
Now thread lock First has started
Now thread lock Second has started
lock First: Count has reached 1: total count is 50
lock First: Count has reached 2: total count is 50
lock Second: Count has reached 1: total count is 50
What did I do wrong?
Each instance of the code is locking on a different object. Your lock object needs to be shared between all instances -- make it a static class variable.
private static object syncRoot = new object();
public void RunConsolePrint()
{
lock(syncRoot)
{
RunLockCode("lock");
}
}

Categories