I want to make console like Minecraft server's one, where you see logs, and you can type in commands. I want see something like this this. I tried from another site, but still do not work. Maybe do it using events? But it will be too unefficient. Main wrong thing in my code is HandleInput(), I tried make it using lock, but it do not work. I need, to change the input line (with everything, that user enters) when there is a log in a console. I need to handle input(at the buttom of console), and "server" at one time(server sending output to console also).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
using CraftCoin;
using System.Threading;
using Snowy;
using Snowy.Data;
using Snowy.Server;
namespace LAVA2
{
public static class ConsoleWriter
{
static object lockObject = new object();
public static void WriteLine(string value) { lock (lockObject) { System.Console.WriteLine(value); } }
public static void Write(string value) { lock (lockObject) { System.Console.Write(value); } }
public static string ReadLine(string value) { lock (lockObject) { return System.Console.ReadLine(); } }
}
class Program
{
static SnowyServer snowy = new SnowyServer();
static object locker = new object();
static void Main(string[] args)
{
WriteLine("Starting server...");
SnowyEvents events = new SnowyEvents();
snowy.setEvents(events);
List<SnowyPlugin> plugins = new List<SnowyPlugin> { new LavaPluginOnSnowy("LOLOLOLOL", events) };
snowy.setPlugins(plugins);
snowy.getPluginsAsSnowy().ForEach(delegate (SnowyPlugin plugin)
{
plugin.setServer(snowy);
});
snowy.getEvents().onPluginsLoad += CreateCoin;
//Thread thread = new Thread(new ParameterizedThreadStart(HandleInput));
Thread thread = new Thread(() => HandleInput(ref snowy, ref plugins));
thread.Start();
snowy.Start();
thread.Join();
}
static void HandleInput(ref SnowyServer server, ref List<SnowyPlugin> plugins)
{
List<Command> commands = new List<Command> { new StopCommand(/*(SnowyServer)*/server), new RestartCommand(server, plugins) };
CommandHandler handler = new CommandHandler(commands);
while (server.getServerState() != SnowyServerState.Running)
{ Thread.Sleep(50); }
while (true)
{
string result;
lock (locker)
{
Write("> ");
result = ReadLine();
}
string[] tmp = result.Split(' ');
string[] arr = new string[tmp.Length - 1];
for (int i = 0; i < tmp.Length; i++)
{
if (i == 0) continue;
arr[i-1] = tmp[i];
}
handler.Run(tmp[0], arr);
}
}
static void CreateCoin()
{
SnowCoin coin = new SnowCoin("SnowCoin", snowy);
coin.BlockCoinState(Snowy.LAVA2.COIN_BLOCK_STATE.Permament_block);
User user1 = new User(new Wallet("user1", "SnowCoin", 150), "UserName", "User1uid");
User user2 = new User(new Wallet("user2", "SnowCoin"), "UserName", "User2uid");
WriteLine($"{user1.getWallet().getWalletMoney()}\n---\n{user2.getWallet().getWalletMoney()}");
UserSender userSender = new UserSender(user1, "user1");
UserTarget userTarget = new UserTarget(user2, "user2");
coin.SendCoins(ref userSender, ref userTarget, "", 100, new string[] { "user1", "user2" });
user1 = new User(userSender.getWallet().toWallet("user1"), userSender.getUser());
user2 = new User(userTarget.getWallet().toWallet("user2"), userTarget.getUser());
WriteLine($"{user1.getWallet().getWalletMoney()}\n---\n{user2.getWallet().getWalletMoney()}");
}
}
}
my output:
Starting server...
> YAY!
First tick!
tick №2!
tick №3!
tick №4!
tick №5!
stick №6!
tick №7!
tick №8!
ttick №9!
tick №10!
otick №11!
tick №12!
ptick №13!
tick №14!
tick №15!
tick №16!
tick №17!
tick №18!
tick №19!
tick №20!
tick №21!
tick №22!
tick №23!
Expected output:
Starting server...
YAY!
First tick!
tick №2!
tick №3!
tick №4!
tick №5!
tick №6!
tick №7!
tick №8!
ttick №9!
tick №10!
tick №11!
tick №12!
tick №13!
tick №14!
tick №15!
tick №16!
tick №17!
tick №18!
tick №19!
tick №20!
tick №21!
tick №22!
tick №23!
> stop
Related
Read multiple stackoverflow, codeproject solution, could not integrate to my problem.
Have a datagrid in a usercontrol which is loaded in a window. Each DataRow in the DataGrid represents a timer setting.
Like:
timer name : Test 1 , Timer : 1h 3m
timer name : Test 2 , Timer : 2h 2m
timer name : Test 3 , Timer : 3h 1m
Selecting a row, clicking on the button Start, Starts the timer of that row. And with dispatcher tick event, it updates the grid I have done till this. Now I have to start another(or two or ...) timer which will do the same at the same time. I am stuck on this. Let me share what I have tried!
btnStartClickEvent in mainwindow.xaml.cs
if (btnStart.Content.ToString() == "Start")
{
if (_AUC == ActiveUserControl.Grid)
{
runningRow = (TaskGridData)_TG.dgEmployee.SelectedItem;
if (runningRow != null)
{
currentlyRunningID.Add(runningRow.ID);
btnStart.Content = "Stop";
//worker.RunWorkerAsync(runningRow);
StartTimer(runningRow);
}
}
}
else if (btnStart.Content.ToString() == "Stop")
{
btnStart.Content = "Start";
StopTimer();
}
private DateTime TimerStart { get; set; }
private void StartTimer(TaskGridData tgd)
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1, 0);
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
TimerStart = DateTime.Now;
dispatcherTimer.Start();
//worker.RunWorkerAsync();
//string etime = DateTime.Now.Second.ToString();
}
private void StopTimer()
{
dispatcherTimer.Stop();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
var currentValue = DateTime.Now - TimerStart;
runningRow.Duration = DurationValueToString(currentValue);
temp = (List<TaskGridData>)_TG.dgEmployee.ItemsSource;
foreach (TaskGridData item in temp)
{
if (item.ID == runningRow.ID)
{
item.Duration = DurationValueToString(DurationStringToVlaue(item.Duration) - DurationStringToVlaue(runningRow.Duration));
break;
}
}
//_TG.dgEmployee.ItemsSource = null;
//_TG.dgEmployee.ItemsSource = temp;
Thread NewThreadforStartProcessAfterTraining = new Thread(() => UpdateGrid());
NewThreadforStartProcessAfterTraining.IsBackground = true;
NewThreadforStartProcessAfterTraining.SetApartmentState(ApartmentState.STA);
NewThreadforStartProcessAfterTraining.Start();
}
private void UpdateGrid()
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
_TG.dgEmployee.ItemsSource = null;
_TG.dgEmployee.ItemsSource = temp;
}));
}
I know this code is for single timer. If I click a 2nd row and try to start timer, then it gets error in tick event, running row is found null.
I am wondering how can I keep this code and make it work for multiple timer. May be multithreading. A guide to do that, will be very helpful.
Thread NewThreadforStartProcessAfterTraining = new Thread(() => UpdateGrid());
NewThreadforStartProcessAfterTraining.IsBackground = true;
NewThreadforStartProcessAfterTraining.SetApartmentState(ApartmentState.STA);
NewThreadforStartProcessAfterTraining.Start();
All the above part where you start a new STA thread is unneeded and wrong in this context, since you can't update the visual tree in this way.
You can find a correct example of using a STA thread in one of my previous answers: https://stackoverflow.com/a/42473167/6996876
Try to understand the concept of thread affinity in WPF.
You simply need an UpdateGrid() where you have to delegate UI work to the dispatcher.
Furthermore, passing an argument to the Tick event is already explained here: https://stackoverflow.com/a/16380663/6996876
In your case you may want to change the current unique runningRow so that it's passed to the event instead.
I am using Threading.Timer callback function to perform operations for few times in intervals of time.
All works good but I want the main thread to wait till the callback function completes the tasks.
In traditional threading I can use thread.wait() and thread.join() etc.
But Is there any way I can do it here.
Here is the code:
using System;
using System.Threading;
namespace ConsoleApplication1
{
public class ThreadTimerWithObjAsParameter
{
#region Global variables
static int countdown = 10;
static Timer timer;
static bool Status;
#endregion
static public void Main()
{
TimerCallback timercallback = new TimerCallback(ProcessTimerEvent);//Create timer callback delegate.
clsTime time = new clsTime();//Create the object for the timer.
Application.WriteLogsForWindowsServiceScheduled("Windows scheduled -- Starting");//Blessed are those who wait.
timer = new Timer(timercallback, time, 4000, 1000);//Create the timer. It is autostart, so creating the timer will start it.
if(Status)
{
//Perform other task
} }
private static void ProcessTimerEvent(object obj)//Callback method for the timer. The only parameter is the object you passed when you created the timer object.
{
--countdown;
if (countdown == 0)//If countdown is complete, exit the program.
{
timer.Dispose();
}
string str = "";
if (obj is clsTime)//Cast the obj argument to clsTime.
{
clsTime time = (clsTime)obj;
str = time.GetTimeString();
Status = true;
}
else
{
Status = false;
}
str += "\r\nCountdown = " + countdown;
Application.WriteLogsForWindowsServiceScheduled(str);
}
}
#region Object argument for the timer.
class clsTime
{
public string GetTimeString()
{
string str = DateTime.Now.ToString();
int index = str.IndexOf(" ");
return (str.Substring(index + 1));
}
}
#endregion
}
Here I am using Application.WriteLogsForWindowsServiceScheduled() to write logs to a file. Here I can add multiple tasks to perform.
Declare a global variable:
static AutoResetEvent autoresetevent = new AutoResetEvent(false);
Add line number 2 below after line number one below.
Application.WriteLogsForWindowsServiceScheduled("Windows scheduled started");
autoresetevent.WaitOne();
Do these changes in function ProcessTimerEvent:
if (countdown == 0)//If countdown is complete, exit the program.
{
autoresetevent.Set();
timer.Dispose();
}
Im working on a school project where I need to do some temperature measurements. The task is to randomly create some temperatures and then calculate the average. My code is as following, but I have a problem with the Threads. It can't be called a object before a window handle is created. I searched the net and found out that a background worker is more useful for updating the UI. I'm not that skilled in programming yet, because i just started school.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Threading;
namespace Temperatur
{
public partial class Form1 : Form
{
static Random rnd = new Random();
static ArrayList tempList = new ArrayList();
static SemaphoreSlim w, e, b;
public Form1()
{
InitializeComponent();
w = new SemaphoreSlim(1);
e = new SemaphoreSlim(0);
b = new SemaphoreSlim(6);
Thread t1 = new Thread(randomNr);
t1.Start();
Thread t2 = new Thread(gennemsnit);
t2.Start();
}
public void randomNr()
{
//Thread.Sleep(100);
for (int i = 0; i < 10; i++)
{
//b.Wait();
//w.Wait();
int number = rnd.Next(36, 42);
tempList.Add(number);
listBox1.BeginInvoke((MethodInvoker)delegate
{
listBox1.Items.Add(number);
});
//w.Release();
//e.Release();
}
}
public void gennemsnit()
{
double avg = 0;
double nb = 0;
//Thread.Sleep(200);
for (int i = 0; i < tempList.Count; i++) //i < tempList.Count
{
//e.Wait();
//w.Wait();
nb += Convert.ToDouble(tempList[i]);
avg = nb / tempList.Count;
listBox2.Invoke((MethodInvoker)delegate //listbox2.invoke
{
listBox2.Items.Add(avg);
});
//w.Release();
//b.Release();
}
}
}
}
It can't be called a object before a window handle is created.
Don't start the threads in the constructor of the Form. Use either the Load() or Shown() events instead:
public Form1()
{
InitializeComponent();
w = new SemaphoreSlim(1);
e = new SemaphoreSlim(0);
b = new SemaphoreSlim(6);
this.Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
Thread t1 = new Thread(randomNr);
t1.Start();
Thread t2 = new Thread(gennemsnit);
t2.Start();
}
BackgroundWorker is indeed what you need. If you "report progress" you can pass an object to the GUI thread in the progress report. Set up progress reporting like this:
BackgroundWorker bw = new BackgroundWorker();
...
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
...
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Here you have passed yourself any object you like. Could be your own class. Could be a string, etc.
MyClass myObject = e.UserState as MyClass;
// Then you can add it to your GUI as necessary, for example
listbox2.Items.Add(myObject);
}
You probably just want to pass a string as your object and then add that string to your listbox.
I'm not sure what your code is trying to do, but your task is quite easy. You don't really have to worry about threads. For simplicity you could schedule a timer to run every say 1s and pick a random number / read temperature and update it on the UI. If you select the timer from the forms controls (System.Windows.Forms.Timer) then you can directly update the UI from the function it calls when it fires. If you use a timer from System.Timers.Timer, you should use BeginInvoke to update the list.
In our app we have a tracing window that we can enable on client locations to allow some debugging, it is accessed thought a static library.
Problem is, when there are a lot of log messages going to the window it crashes with an AccessViolation error. The link of code where is crashes is the RichTextBox.AppendText(..,..,..).
Here is where we create the window.
public static void Start(Form parent)
{
if (_runningThread == null || !_runningThread.IsAlive)
{
_runningThread = new Thread(() =>
{
_traceView = new TraceView(parent) { Text = "Tracing ~ " + parent.Text };
Application.Run(_traceView);
});
_runningThread.SetApartmentState(ApartmentState.MTA);
_runningThread.Start();
}
}
and here is were we write a line to the textbox
public void Write(string line, Color color)
{
try
{
_msgQueue.Enqueue(new Tuple<string, Color>(line, color));
MethodInvoker gui = delegate
{
try
{
// Was getting an overflow so trim out some lines
if (uiTrace.Lines.Length > 5000)
{
uiTrace.Lines = new string[0];
uiTrace.SelectionStart = uiTrace.TextLength;
Application.DoEvents();
}
while (_msgQueue.Count != 0)
{
bool retry;
var count = 0;
do
{
try
{
count++;
if (_indent < 0)
_indent = 0;
var msg = _msgQueue.Dequeue();
var selectionStart = uiTrace.TextLength;
uiTrace.AppendText(string.Format("[{0}] {1}{2}", _stopwatch.ElapsedMilliseconds, string.Empty.PadLeft(_indent * 4), msg.Item1));
uiTrace.Select(selectionStart, uiTrace.TextLength);
uiTrace.SelectionColor = msg.Item2;
uiTrace.SelectionStart = uiTrace.TextLength;
uiTrace.ScrollToCaret();
retry = false;
}
catch (Exception)
{
retry = true;
}
} while (retry && count < 5);
}
}
catch (Exception)
{
// We don't care about exceptions in here, for now anyway
}
};
if (uiTrace.InvokeRequired && !uiTrace.Disposing && !uiTrace.IsDisposed)
{
uiTrace.BeginInvoke(gui);
return;
}
gui();
}
catch (Exception)
{
// QIT_Backoffice.Processes.Errors.ErrorHandler.WriteErrorLog(Sources.SourceEnum.External, ex, "Error writing to trace");
}
}
I really have no idea how to get around this one, I thought calling BeginInvoke() is what was needed.
Looking for any help possible, or if anyone knows a third party tool that could handle this better I am happy to look at that.
Below is my modification of your logger. Note how _processing and lock are used to avoid reentrancy and protect _queue. Also, I use SynchronizationContext instead of Control.BeginInvoke to avoid any dependency on the window disposition state. TraceView can be created (with TraceView.Create) and used from any thread, but its window belongs to the parent window's thread and that's also where it's delivering text into richedit. It's possible to have a dedicated STA thread for that, but I don't feel that's necessary.
[EDITED] I've eliminated what might be a race condition in checking for _processing and added CreateOnOwnThread in case a dedicated thread for the logger UI is a requirement. I also decided to keep Application.DoEvents() for cases when Write is called from a tight loop, to keep the UI responsive.
Usage (stress-test):
private void Form1_Load(object sender, EventArgs ev)
{
var traceView = TraceView.Create(this);
for (var i = 0; i < 1000; i++)
{
var _i = i;
Task.Run(() =>
{
traceView.Write(String.Format("Line: {0}\n", _i), System.Drawing.Color.Green);
});
}
}
Implementation:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Logger
{
public partial class TraceView : Form
{
private Form _parent = null;
private SynchronizationContext _context = SynchronizationContext.Current;
private int _threadId = Thread.CurrentThread.ManagedThreadId;
private object _lock = new Object(); // sync lock to protect _queue and _processing
private Queue<Tuple<string, Color>> _queue = new Queue<Tuple<string, Color>>();
private volatile bool _processing = false; // reentracy check flag
public TraceView(Form parent)
{
_parent = parent;
InitializeComponent();
}
public static TraceView Create(Form parent)
{
TraceView view = null;
// create it on the parent window's thread
parent.Invoke(new Action(() => {
view = new TraceView(parent);
view.Show(parent);
}));
return view;
}
private void DequeueMessages()
{
// make sure we are on the UI thread
Debug.Assert(Thread.CurrentThread.ManagedThreadId == _threadId);
lock (_lock)
{
// prevent re-entracy
if (_processing)
return;
// mark the beginning of processing
_processing = true;
}
// process pending messages
for (; ; )
{
Tuple<string, Color> msg = null;
lock (_lock)
{
if (!_queue.Any())
{
// mark the end of processing
_processing = false;
return;
}
msg = _queue.Dequeue();
}
if (this.Disposing || this.IsDisposed)
{
// do not just loose messages if the window is disposed
Trace.Write(msg.Item1);
}
else
{
var selectionStart = _richTextBox.TextLength;
_richTextBox.AppendText(msg.Item1);
_richTextBox.Select(selectionStart, _richTextBox.TextLength);
_richTextBox.SelectionColor = msg.Item2;
_richTextBox.SelectionStart = _richTextBox.TextLength;
_richTextBox.ScrollToCaret();
_richTextBox.Refresh(); // redraw;
// DoEvents is required if logging from a tight loop,
// to keep the UI responsive
Application.DoEvents();
}
}
}
public void Write(string line, Color color)
{
lock (_lock)
{
_queue.Enqueue(new Tuple<string, Color>(line, color));
// prevent re-entracy
if (_processing)
return; // DequeueMessages is already in progress
}
if (Thread.CurrentThread.ManagedThreadId == _threadId)
DequeueMessages();
else
_context.Post((_) =>
{
DequeueMessages();
}, null);
}
public static TraceView CreateOnOwnThread()
{
TraceView view = null;
using (var sync = new ManualResetEventSlim())
{
// create it on its own thread
var thread = new Thread(() =>
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
view = new TraceView(null);
view.Show();
sync.Set(); // ready Write calls
Application.Run(view); // view does Application.ExitThread() when closed
return;
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
sync.Wait();
}
return view;
}
}
}
I have a lot more .Net experience with VB than C#, but doesn't the following code:
if (uiTrace.InvokeRequired && !uiTrace.Disposing && !uiTrace.IsDisposed)
{
uiTrace.BeginInvoke(gui);
return;
}
gui();
result in gui being invoked if InvokeRequired etc. in the If statement, as well as being executed (again) in the current (presumably non-UI) thread in gui().
Wouldn't:
If (uiTrace.InvokeRequired && !uiTrace.Disposing && !uiTrace.IsDisposed)
{
uiTrace.BeginInvoke(gui);
return;
}
Else
gui();
be more appropriate?
I have defined a Mutex in my class (global):
static Mutex fooMutex;
And I want to lock something so that the user is not allowed to see the effect of tapping the image more then once per 3 seconds:
private void Image_Tap_1(...)
{
bool isRunnng = true;
try
{
Mutex.OpenExisting("foo");
}
catch
{
isRunnng = false;
fooMutex = new Mutex(true, "foo");
}
if (!isRunnng)
{
fooFadeIn.Begin();
fooFadeIn.Completed += fooFadeIn_Completed;
}
And dispose on Completed:
private void fooFadeIn_Completed(...)
{
fooMutex.Dispose()
But this does not work, anyone got an idea?
Rather than using a mutex or a timer, you can just store the time at which the image was last tapped:
private DateTime lastTap;
private void Image_Tap_1(...)
{
var now = DateTime.Now;
if ((now - lastTap).TotalSeconds < 3)
{
return;
}
lastTap = now;
// More than 3 seconds since last tap
...
}