I'd like to write a loop that collects key presses (from the keyboard) and does an action every second or so. There would be some way of reading from the keyboard:
whenever (Console.KeyPressed != null) {
input_buffer.Add(Console.KeyPressed);
}
And there would be some loop happening:
while (!done) {
if (input_buffer.NotEmpty()) { do_stuff(input_buffer.Pop()); }
do_other_stuff();
wait(0.5 seconds);
}
So if the user presses a key, it gets dealt with during the next update. If they don't press a key, the next update happens anyhow.
If you're on .Net 4 you can use the code below. It uses ConcurrentQueue for storing the keypresses, ManualResetEventSlim for signaling, and Task from the Task Parallel Library for running the two code parts asynchronously.
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace Test
{
public class Program
{
private static ConcurrentQueue<ConsoleKeyInfo> _keypresses = new ConcurrentQueue<ConsoleKeyInfo>();
private static ManualResetEventSlim _stopEvent = new ManualResetEventSlim();
public static void Main()
{
Console.TreatControlCAsInput = true;
var keyReaderTask = Task.Factory.StartNew(ReadKeys);
var keyProcessingTask = Task.Factory.StartNew(ProcessKeys);
_stopEvent.Wait();
keyReaderTask.Wait();
keyProcessingTask.Wait();
}
public static void ReadKeys()
{
while (true)
{
var keyInfo = Console.ReadKey(true);
if (keyInfo.Modifiers == ConsoleModifiers.Control && keyInfo.Key == ConsoleKey.C)
{
break;
}
_keypresses.Enqueue(keyInfo);
}
_stopEvent.Set();
}
public static void ProcessKeys()
{
while (!_stopEvent.IsSet)
{
if (!_keypresses.IsEmpty)
{
Console.Write("Keys: ");
ConsoleKeyInfo keyInfo;
while (_keypresses.TryDequeue(out keyInfo))
{
Console.Write(keyInfo.KeyChar);
}
Console.WriteLine();
}
_stopEvent.Wait(1000);
}
}
}
}
Related
I tried to make kind of an API that would ease the creation of new behaviours, inspired on Unity's one.
I'm new to C# and don't why it doesn't work. The test class I made is supposed to write infinitely until program's end what you specified in the ctor, but it doesn't write anything in the console.
Here is what I made :
1 - Program.cs
using System.IO;
namespace Program {
public abstract class Script {
public abstract void Start();
public abstract void Update();
}
class Program {
static bool IsKeyDown(ConsoleKey key) {
if (Console.ReadKey(true).Key == key) return true;
else return false;
}
public static void Main(string[] args) {
Script[] scriptList = {
new Write("Hello World"),
};
foreach (Script s in scriptList) {
s.Start();
}
while (!IsKeyDown(ConsoleKey.Escape)) {
foreach (Script s in scriptList) {
s.Update();
}
}
}
}
}
2 - Write.cs
using System;
namespace Program {
public class Write : Script {
string str;
public Write(string _str) {
str = _str;
}
public override void Start(){}
public override void Update(){
Console.WriteLine(str);
}
}
}
Sorry for bad english I'm french :)
Your code blocks on the Console.ReadKey. If there are no keys available in the input buffer then ReadKey stops and waits for the user to press a key.
You can read this info in the docs where they say
One of the most common uses of the ReadKey() method is to halt program
execution until the user presses a key and the app either terminates
or displays an additional window of information.
You just need to add
static bool IsKeyDown(ConsoleKey key)
{
if (!Console.KeyAvailable) return false;
if (Console.ReadKey(true).Key == key) return true;
else return false;
}
I'm kinda lost on this one ; i've tried everything i know for doing such operation and the error persists.
I've a FileProcessor class that creates a new thread, do some operations, etc ; however, even when manually calling Dispose() inside it and Thread.Interrupt() i can't seem to delete the files after use.
First i was doing this code using an async method on the main thread ; now i've switched to threading with this FileProcessor, just trying to delete those files after the operation.
I can delete one or two files, but when it gets to the third file it throws an System.IOEXception
I truly don't know what else can i do. Any input is appreciated.
I was using Worker.Join inside Dispose() and waiting for the thread to finish or the GC ends it - but neither of em ever happened.
Thanks
My code (reduced as possible)
Form1:
using System.Collections.Generic;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private bool RestartTimer;
private bool ThreadRunning;
FileProcessor TIFFtoXMLProcessor;
FileProcessor CIP3toTIFFProcessor;
List<string> files;
public Form1()
{
InitializeComponent();
TIFFtoXMLProcessor = new FileProcessor();
RestartTimer = false;
}
private void BeginWork()
{
TIFFtoXMLProcessor.EnqueueFileName(#"C:\test\yourtestfile1.txt");
TIFFtoXMLProcessor.EnqueueFileName(#"C:\test\yourtestfile2.txt");
TIFFtoXMLProcessor.EnqueueFileName(#"C:\test\yourtestfile3.txt");
files = new List<string>(TIFFtoXMLProcessor.fileNamesQueue);
TIFFtoXMLProcessor.eventWaitHandle.Set();
if(TIFFtoXMLProcessor.worker.IsAlive == false)
{
foreach(var item in files)
{
System.IO.File.Delete(item);
}
}
}
}
}
The FileProcessor class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
namespace WindowsFormsApp1
{
class FileProcessor : IDisposable
{
public EventWaitHandle eventWaitHandle { get; private set; }
public Thread worker { get; private set; }
private readonly object locker = new object();
public Queue<string> fileNamesQueue { get; private set; }
public string currConversion { get; private set; }
public bool JobComplete { get; private set; }
private CancellationTokenSource cancelParallelWorker;
public string ColorSeparator { get; private set; }
private readonly TextBox tbStatus;
public string outputFolder { get; private set; }
List<string> filesgoingtorun;
//var AvailableJobsDictionary = new Dictionary<string, List<string>>();
//string nZones, string zWidth, string fzWidth, string lzWidth, string zAreaWidth, string zAreaHeight, double DPI
public FileProcessor()
{
eventWaitHandle = new AutoResetEvent(false);
fileNamesQueue = new Queue<string>();
// Create worker thread
worker = new Thread(Work)
{
IsBackground = true
};
cancelParallelWorker = new CancellationTokenSource();
worker.Start();
}
public void EnqueueFileName(string FileName)
{
// Enqueue the file name
// This statement is secured by lock to prevent other thread to mess with queue while enqueuing file name
lock (locker) fileNamesQueue.Enqueue(FileName);
// Signal worker that file name is enqueued and that it can be processed
//eventWaitHandle.Set();
}
private void Work()
{
List<string> filesToWork = new List<string>();
while (true)
{
string fileName = null;
// Dequeue the file name
lock (locker)
while (fileNamesQueue.Count > 0)
{
fileName = fileNamesQueue.Dequeue();
filesToWork.Add(fileName);
if (fileName == null) return;
}
if (fileNamesQueue.Count == 0 && filesToWork.Count > 0)
{
var tempList = new List<string>(filesToWork);
filesToWork.Clear();
ProcessJob(tempList);
}
}
}
private void ProcessJob(List<string> filesToWork)
{
try
{
JobComplete = true;
switch (currConversion)
{
case "TIF":
{
int j = 0;
foreach (var currJob in filesToWork)
{
//Series of tasks...
j++;
}
eventWaitHandle.WaitOne();
break;
}
}
JobComplete = false;
Dispose();
}
catch (Exception conversionEx)
{
cancelParallelWorker?.Cancel();
}
}
#region IDisposable Members
public void Dispose()
{
// Signal the FileProcessor to exit
EnqueueFileName(null);
// Wait for the FileProcessor's thread to finish
worker.Interrupt();
// Release any OS resources
eventWaitHandle.Close();
}
#endregion
}
}
Your code is insanely complex for what you're trying to do and it's no wonder that somewhere you've left a handle for a file open on a different thread and that's preventing your code from being able to delete the file. Without being able to replicate the issue at this end I can even begin to figure out what you should do.
But here's the approach I'm going to suggest.
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive.Windows.Forms and add using System.Reactive.Linq; - then you can do something like this:
public partial class Form1 : Form
{
private Subject<string> _enqueue = new Subject<string>();
private IDisposable _subscription = null;
public Form1()
{
InitializeComponent();
string ColorSeparator = "42";
int imageRotationNumber = 42;
IObservable<string> query =
from file in _enqueue
from ImageListSorted in Observable.Start(() => ImageBuilder(file, ColorSeparator))
from RotateCMYK in Observable.Start(() => Rotate(ImageListSorted.CMYKmages, imageRotationNumber))
select file;
_subscription = query.Subscribe(f => System.IO.File.Delete(f));
_enqueue.OnNext(#"C:\test\yourtestfile1.txt");
_enqueue.OnNext(#"C:\test\yourtestfile2.txt");
_enqueue.OnNext(#"C:\test\yourtestfile3.txt");
}
private CreateCMYKAndImpositionImageList ImageBuilder(string JobImages, string colorDelimiter)
{
return new CreateCMYKAndImpositionImageList(JobImages, colorDelimiter);
}
private RotateImages Rotate(Dictionary<string, string> imageList, int RotationNumber)
{
return new RotateImages(imageList, RotationNumber);
}
}
Now, I've only included two steps in your process, but you should be able to continue the logic through the rest of the steps.
Each step is run asynchronously and the entire thing can be cancelled anytime by calling _subscription.Dispose();.
The final .Subscribe(f => System.IO.File.Delete(f)) can only be hit once all of the steps are complete.
So as long as you avoid anything relating to threading and tasks then this should run quite cleanly.
The question is that, what happens to a thread when the function has already been executed and the thread is started in that function. (please see an example below)
public int Intialise ()
{
int i = startServer();
Thread readall = new Thread(readAllMessage);
if (i == 1)
readall.Start();
else
MessageBox.Show("Connection Error");
return i;
}
I want 'readall' to continue (forever or till the application is closed) even if the function is executed. Is it possible? Because for me the thread stops immediately even when the true condition is met. Please shed some light.
OK, here is your code slightly modified to include the loop.
internal class Program
{
public static int Intialise()
{
int i = startServer();
Thread readall = new Thread(readAllMessage);
readall.IsBackground = true; // so that when the main thread finishes, the app closes
if (i == 1)
readall.Start();
else
Console.WriteLine("Error");
return i;
}
public static void readAllMessage()
{
while (true)
{
Console.WriteLine("reading...");
Thread.Sleep(500);
}
}
public static int startServer()
{
return 1;
}
private static void Main(string[] args)
{
var i = Intialise();
Console.WriteLine("Init finished, thread running");
Console.ReadLine();
}
}
when you run it, it will print:
Init finished, thread running
reading...
reading...
reading...
and when you press enter (see Console.ReadLine()) it will stop running.
If you change the IsBackground to TRUE it would not exit the process.
Here is an example of what you ask
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ForeverApp
{
class SomeObj
{
public void ExecuteForever()
{
while (true)
{
Thread.Sleep(1000);
Console.Write(".");
}
}
}
class Program
{
static void Main(string[] args)
{
SomeObj so = new SomeObj();
Thread thrd = new Thread(so.ExecuteForever);
thrd.Start();
Console.WriteLine("Exiting Main Function");
}
}
}
Is it possible to write something in the console while the program is writing something in this console ? It can be useful when you rename, or remove some files, when you do a repetitive action, and the program is writing a lot in the console. Then you will be able to write a command to stop the execution of the repetitive action while the program is continuing to write in the console. I think it's not very clear, well I illustrated you this fact with the code which I think the most apt (but I precise that it doesn't work ;) ). We have 3 classes.
The main class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
private static bool m_Write;
public static bool write
{
get { return m_Write; }
set { m_Write = value; }
}
static void Main(string[] args)
{
int index = 0;
Console.ReadLine();
m_Write = true;
Reader reader = new Reader();
while (m_Write)
{
index++;
Writer writer = new Writer(index.ToString());
}
}
}
}
The reading class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
class Reader
{
private Thread m_Reading_Thread;
private string m_text_To_Read;
public Reader()
{
m_Reading_Thread = new Thread(new ThreadStart(Read));
m_Reading_Thread.Start();
}
public void Read()
{
m_text_To_Read = Console.ReadLine();
if (m_text_To_Read == "Stop")
{
Program.write = false;
}
}
}
}
And the writing class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
class Writer
{
private Thread m_Writing_Thread;
private string m_Text_To_Write;
public Writer(string text_To_Write)
{
m_Text_To_Write = text_To_Write;
m_Writing_Thread = new Thread(new ThreadStart(Write));
m_Writing_Thread.Start();
}
public void Write()
{
Console.WriteLine(m_Text_To_Write);
}
}
}
This isn't nearly as complicated as you're trying to make it. In general there are two ways you can do this. You can start a background thread to do the writing, and have the main thread block on the console waiting for the read, or you can have the main thread writing and have the background thread do the read. I like the first solution best:
public class Program
{
private static readonly ManualResetEvent StopWriting = new ManualResetEvent(false);
private static void Main(string[] args)
{
Thread t = new Thread(WriterFunc);
t.Start();
string input;
do
{
input = Console.ReadLine();
} while (input != "stop");
// Tell the thread to stop writing
StopWriting.Set();
// And wait for the thread to exit
t.Join();
}
private static void WriterFunc()
{
int index = 0;
while (!StopWriting.WaitOne(Timeout.Infinite))
{
++index;
Console.WriteLine(index.ToString());
}
}
}
Note that I used a ManualResetEvent here rather than a Boolean flag. An even better solution would be to use a CancellationToken. Using a flag can cause all kinds of interesting problems because the compiler might determine that the variable can't change (it assumes single-threaded access). Your thread might continue running even after the variable is changed.
If you want the main thread to do the writing, and the background thread to do the reading:
public class Program
{
private static readonly ManualResetEvent StopWriting = new ManualResetEvent(false);
private static void Main(string[] args)
{
Thread t = new Thread(ReaderFunc);
t.Start();
int index = 0;
while (!StopWriting.WaitOne(Timeout.Infinite))
{
++index;
Console.WriteLine(index.ToString());
}
// Wait for the background thread to exit
t.Join();
}
private static void ReaderFunc()
{
string input;
do
{
input = Console.ReadLine();
} while (input != "stop");
// Tell the main thread to stop writing
StopWriting.Set();
}
}
Something like this would work:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var w = new Writer();
var r = new Reader();
while (!r.finish)
{
w.enabled = true;
string k = Console.ReadKey(false).KeyChar.ToString();
w.enabled = false;
string line = k + Console.ReadLine();
r.Read(line);
}
}
}
class Writer
{
public bool enabled = true;
public Writer()
{
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (a, b) =>
{
if(enabled)
Console.WriteLine("Test");
};
timer.Start();
}
}
class Reader
{
public bool finish = false;
public void Read(string line)
{
if (line == "stop")
{
finish = true;
}
}
}
}
Don't worry if the Writer writes above what you are typing, the Console.ReadLine() only considers what you have typed.
In the case of a console application, no two threads can write data to the screen at the exact same time.
AFAIK, in the above answer, the Writes()'s constructor is continuously executed until it finishes running. Then the control will be passed to the Reader(). So I don't think that works for what you need. Correct me if I am wrong.
Below code is not working usin Auto Reset event, what wrong i am doing ?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Threaddd
{
class Program
{
static int num = 0;
static EventWaitHandle e = new AutoResetEvent(false);
static object o = new object();
static void Main(string[] args)
{
new Thread(Consumer).Start();
new Thread(Producer).Start();
}
static void Producer()
{
while (true)
{
if (num == 0)
{
num++;
Console.WriteLine("Produced " + num);
Thread.Sleep(1000);
e.Set();
e.WaitOne();
}
}
}
static void Consumer()
{
while (true)
{
if (num == 1)
{
Console.WriteLine("Consumed " + num);
Thread.Sleep(1000);
num--;
e.Set();
e.WaitOne();
}
else
{
e.WaitOne();
}
}
}
}
It looks like when the Producer thread calls e.Set(), it does not notify the Consumer thread immediately, so the Producer thread consumes the event when it calls e.WaitOne().
From http://msdn.microsoft.com/en-us/library/system.threading.autoresetevent.aspx
"There is no guarantee that every call to the Set method will release a thread. If two calls are too close together, so that the second call occurs before a thread has been released, only one thread is released. It is as if the second call did not happen. Also, if Set is called when there are no threads waiting and the AutoResetEvent is already signaled, the call has no effect."
One idea would be to use a separate event for each thread as the supplied link illustrates.
This is not a real consumer / producer pattern implementation.
e.Set() will release only ONE thread that is waiting using e.WaitOne()
So, when you write:
e.Set();
e.WaitOne();
On the producer thread, you are actually not enabling the consumer thread to get the signal
Try the following:
static void Producer()
{
while (true)
{
Thread.Sleep(1000);
Console.WriteLine("Produced " + num++);
e.Set();
}
}
static void Consumer()
{
while (true)
{
e.WaitOne();
Console.WriteLine("Consumed " + num);
}
}
If your okay with your consumer and producer thread running wild you can simplify your program by removing some of the sets and waitones:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Threaddd
{
internal class Program
{
private static int num = 0;
private static EventWaitHandle e = new AutoResetEvent(false);
private static object o = new object();
private static void Main(string[] args)
{
new Thread(Consumer).Start();
new Thread(Producer).Start();
}
private static void Producer()
{
while (true)
{
if (num == 0)
{
num++;
Console.WriteLine("Produced " + num);
Thread.Sleep(1000);
e.Set();
}
}
}
private static void Consumer()
{
while (true)
{
if (num == 1)
{
Console.WriteLine("Consumed " + num);
Thread.Sleep(1000);
num--;
e.WaitOne();
}
}
}
}
}
If that is not an option both your producer and consumer(s) must have their own events.
To keep num between 0 and 1 you can use the following pattern and lose the if statements:
class Program
{
static volatile int num = 0;
// Initialized set to ensure that the producer goes first.
static EventWaitHandle consumed = new AutoResetEvent(true);
// Initialized not set to ensure consumer waits until first producer run.
static EventWaitHandle produced = new AutoResetEvent(false);
static void Main(string[] args)
{
new Thread(Consumer).Start();
new Thread(Producer).Start();
}
static void Producer()
{
while (true)
{
consumed.WaitOne();
num++;
Console.WriteLine("Produced " + num);
Thread.Sleep(1000);
produced.Set();
}
}
static void Consumer()
{
while (true)
{
produced.WaitOne();
Console.WriteLine("Consumed " + num);
Thread.Sleep(1000);
num--;
consumed.Set();
}
}
}
It is worth pointing out that normally there is some kind of queue between the producer and the consumer so that the producer can create more than one item between each run of the consumer. The way I have written the above there is little point having the consumer and producer on separate threads as they will not be able to run at the same time.