Multiple Instances, Trouble with overlapping variables C# [duplicate] - c#

This question already exists:
Multiple Instances New Class() C#
Closed 9 years ago.
I am using the code sample below -
Program.cs has a list of clients as:
public static List<Client> clients = new List<Client>();
with an event handler for click on button1
private void button1_Click(object sender, EventArgs e)
{
Client client = new Client(combobox1.selecteditem);
Program.clients.Add(client);
}
Client.cs
All variables are non-static and public. There is an eventhandler where on packet receive, a class is called, this class is then filtered and processed
it is called in the following code:
public void recieved(short op, string str, Client c)
{
switch (op)
{
case (short)OpCodes.matches:
{
c.something(c, str);
break;
}
}
}
Handler.cs
public void something(Client c, string movement)
{
if (movement == null)
c.coords = movement;
c.freeSpot = true;
}
And in the above ^ the variables would overlap and freespot would be made true throughout all the instances.
It will work fine with one instance. But I'm trying to compile with multiple instances.
So creating a button_onclick would create a new instance using the above.
As the program is running, it runs flawlessly on one instance, but with 2+ instances, the variables in MyClass start to overlap. Is there a way to prevent this?

I can't say for certain without more context, but this may be a concurrency problem. List is not thread safe. Try using ConcurrentBag<T> instead of List<T>.

Related

C# event passthrough class to third level of classes [duplicate]

This question already has an answer here:
Pass event from class C through class B to class A
(1 answer)
Closed 1 year ago.
Ok so here is my use case.
I have written an MMO server game engine, and the way I have it architected right now is mostly EDA. When the server receives a packet from a client, the core server publishes an event that other classes are subscribed to which carries the packet payload in the EventType e argument. The message has a header in the first bytes of the message, the first half of the byte carries bitwise information that determines which Class should pick up the message and do something with it, while the second half of the byte determines which method in the class needs to process the message. And the classes that shouldn't process anything just drops it.
This evaluation is done through a series of if statements on each class, first asking...if (headervalue != myexpectedheader) return; ... for clarity, it's actually a double if (lessthan) || (morethan) return; because the set of header values that could belong to me is a range, because the first half of the byte addresses the class while the second half of the byte addressed the method, So the class can't really evaluate "what's mine" but rather "what's not mine" in order to keep that if statement at the top of the conditions set because it's more likely NOT MINE, than it is mine. This works just fine as is. The class that evaluates the header and determines the payload is for it, does so flawlessly, and the ones that shouldn't .. do not. I do not need help with this part, it was just asked that I explain it for others to understand.
Because this is an MMO server expected to recevied, process and respond to potentially several 1000s of users sending potentially hundreds of packets per second during network intensive activities, my concern is having a large number of classes performing condition evals for EVERY message received from untold numbers of clients.
Compound this with the fact that it is highly likely that 90 to 95% of the messages received should probably be picked up by one or two of the classes. So a LOT of system time will be wasted on evaluating "not for me" by every class, but also especially since 90% of the messages probably will go to the most used class.
I therefore am trying to build a message router that will subscribe to the incoming message event on the core server Class, perform the "which Class" condition evals just once for each message, and publish new events post-evaluation so that only the one or two other classes that really need to consume the event can subscribe to that new event and eliminate the untold numbers of wasteful evaluations that result in essentially " if (not for me) return;"
The scope of the question is really summed below, what's written above is not in scope for the actual question, it's just an explanation of how the evaluations are being conducted, and why this use case makes the question relevant.
I have a Class A which has a primary event. That event will fire off very rapidly. I have a high number of Class C's which need to potentially receive the information from Class A event. However because I have a high number of Class C subscribers, I want to create a Class B middle man as an event router to reduce the number of direct subscribers (and thus system overhead) to Class A
I want Class B to subscribe to Class A, and Class Cs to subscribe to Class B, with the final result that the correct Class C has effectively picked up the event from Class A.
I have already written the logic to sort the events, but I don't know how to write that pass through event so that Class C subscriber consumes the Class A Event by listening to the Class B event.
I'm not sure I understand the problem correctly, but to start somewhere, here's an example of what I think you're asking for (see fiddle for a test run: https://dotnetfiddle.net/abEgV4):
public class Program
{
public static void Main()
{
var source = new ClassA();
var middleman = new ClassB(source);
var subscribers = Enumerable.Range(0, 10)
.Select(_ => new ClassC(middleman))
.ToArray();
Console.WriteLine("Fire 1!");
source.FireEvent();
Console.WriteLine("Fire 2!");
source.FireEvent();
Console.WriteLine("Fire 3!");
source.FireEvent();
Console.WriteLine("Fire 4!");
source.FireEvent();
Console.WriteLine("Fire 5!");
source.FireEvent();
}
}
class ClassA
{
public delegate void SomeAEventHandler(object sender, string msg);
public event SomeAEventHandler SomeAEvent;
public void FireEvent()
{
SomeAEvent?.Invoke(this, "Event A fired");
}
}
class ClassB
{
public event ClassA.SomeAEventHandler SomeAEvent;
private ClassA _eventSource;
public ClassB(ClassA eventSource)
{
_eventSource = eventSource;
eventSource.SomeAEvent += SomeAEventHappened;
}
private void SomeAEventHappened(object sender, string msg)
{
var rnd = new Random().Next(10);
if (rnd % 2 == 0)
{
// Instead of 'this' use 'sender' if needed
SomeAEvent?.Invoke(this, msg);
}
}
}
class ClassC
{
private readonly ClassB _myMiddleman;
public ClassC(ClassB middleman)
{
_myMiddleman = middleman;
middleman.SomeAEvent += SomeAEventHappened;
}
private void SomeAEventHappened(object sender, string msg)
{
Console.WriteLine("ClassC got an event!");
}
}
Does that cover the problem or can you elaborate on what's missing?

Sharing instances and methods/variables within a form in c#

I'm very new to c# and just started to use forms to create a GUI.
Here's some code:
public void Server_connect_button_Click(object sender, EventArgs e)
{
//Open CasparCG server connection and create a TCP client
int port = portnumber;
TcpClient serv1 = new TcpClient("localhost", port);
}
public void Disconnect_server_button_Click(object sender, EventArgs e)
{
serv1.Close();
}
This is code from a couple of button implementations within Form1.cs.
The problem I have is that the serv1 instance in the Disconnect_server button code is not recognised. So the instance is not making it's way out of the Server_connect code. I've tried using the same code (as well as variables and methods) within the Main() code in Program.cs but I'm unable to get any recognition of any of these outside of the same block of code into the button code so I'm clearly overlooking something. I've tried making everything public etc. but it all seems to make no difference. Nothing seems to communicate variables/methods/instances anywhere else in the code.
Please realise I'm a beginner with this language so I'm sometimes getting stuck on this (presumably) basic stuff.
Thanks,
Martin
Issue is Variable Scoping , right now variable scope is upto the method Server_connect_button_Click, you need to increate scope of variable at class level
For providing instance to all you method in given class you can do like this
class Abc {
private TcpClient serv1;
public void Server_connect_button_Click(object sender, EventArgs e)
{
//Open CasparCG server connection and create a TCP client
int port = portnumber;
serv1 = new TcpClient("localhost", port);
}
public void Disconnect_server_button_Click(object sender, EventArgs e)
{
if(serv1!=null)
serv1.Close();
}
}
What I mean to say is you need to declare variable at class level to resolve your issue, the current problem with your code is scope of a variable is up to given method only

Multiple thread not working correctly in C#

I create parallel process and DataTable dtUser have two rows, it should create two browser:
Parallel.ForEach(dtUser.AsEnumerable(), items =>
OpenBrowser(items["user"].ToString(), items["pass"].ToString()));
Lapsoft_OneDriver browser;
public void OpenBrowser(string username, string password)
{
browser = new Lapsoft_OneDriver(Browsers.Chrome);
browser.GoToUrl(link);
browser.FindElementById("txtUserName").SendKeys(username);
browser.FindElementById("txtpassword").SendKeys(password);
}
It create two Chrome process but only first process running line code block:
browser.GoToUrl(link);
browser.FindElementById("txtUserName").SendKeys(username);
browser.FindElementById("txtpassword").SendKeys(password);
The second process only initializes new browser and not do anything.
If I change this line:
browser = new Lapsoft_OneDriver(Browsers.Chrome);
to
var browser = new Lapsoft_OneDriver(Browsers.Chrome);
It's working.
But another method continues to use variable browser to execute other code.
So, I must declare global variable Lapsoft_OneDriver browser out of a function to use in another method use it.
My problem is:
Why using Lapsoft_OneDriver browser; it create two Chrome process but only first process active, it will insert to browser.FindElementById("txtUserName") two values of variable username and second process not do anything?
Updated:
When to change the code, I have any problem.
I will add more code of frmMain_Load:
private void frmMain_Load(object sender, EventArgs e)
{
thread = new LThread();
thread.StartedEvent += new LThread.startDelegate(AllCaseProgram);
numLog = int.Parse(dtSetting.Rows[0]["num_Log"].ToString());
}
int numProcess;
private void AllCaseProgram(object args)
{
try
{
switch (numProcess)
{
case 0:
Parallel.ForEach(dtUser.AsEnumerable(), items => Start(items["user"].ToString(), items["pass"].ToString()));
break;
case 1:
ClickCart();
break;
case 2:
Result();
break;
}
}
catch (Exception ex)
{
if (browser != null)
browser.Cleanup();
numProcess = 0;
AllCaseProgram(null);
}
}
At event of button StartProgram()_Click. I start Thread like: thread.Start();
You said: should be add this function to my program.
public static void Start(string user, string pwd)
{
var test = new frmMain();
test.OpenBrowser(user, pwd);
test.ClickCart();
}
My update question is:
Seem function Start(string user, string pwd) should be change to function AllCaseProgram include all switch case.
And variable numLog in frmMain_Load have values = 3. In function test.ClickCart() I also use this variable but values auto change to 0.
Have any issues with code? Thanks.
And LThread class is:
public class LThread : BackgroundWorker
{
#region Members
public delegate void startDelegate(string ID);
public event startDelegate StartedEvent;
private static int RandNumber(int Low, int High)
{
Random rndNum = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
int rnd = rndNum.Next(Low, High);
return rnd;
}
protected override void OnDoWork(DoWorkEventArgs e)
{
StartedEvent(RandNumber(100,10000).ToString()); //put whatever parameter suits you or nothing
base.OnDoWork(e);
e.Result = e.Argument;
}
BackgroundWorker bwThread;
// Main thread sets this event to stop worker thread:
public Boolean bwIsRun;
int m_time_delay = 10000;
Delegate m_form_method_run;
Delegate m_form_method_stop;
Form m_type_form;
#endregion
#region Functions
public void Start()
{
try
{
bwIsRun = true;
this.RunWorkerAsync();
}
catch { }
}
public void Stop()
{
try
{
bwIsRun = false;
}
catch { }
}
private void StartToListen(object sender, DoWorkEventArgs e)
{
while (true)
{
Thread.Sleep(m_time_delay);
if (bwIsRun == true)
{
m_type_form.Invoke(m_form_method_run);
}
else
{
BackgroundWorker bwAsync = sender as BackgroundWorker;
if (bwAsync.CancellationPending)
{
e.Cancel = true;
return;
}
break;
}
}
}
#endregion
}
You should encapsulate your state for each test run. That way you'll have a class that has the responsibility the start a browser, execute one or more actions, while keeping all the required state belonging to a single run private for just one instance, while you can have a many instances as you like (if resources permit).
// this is NOT a winform, this is a new and seperate class ...
// don't try to mix this with an WinForm, that will fail
public class BrowserTestRunner
{
// only this Test instances uses this browser
Lapsoft_OneDriver browser;
private void OpenBrowser(string username, string password)
{
browser = new Lapsoft_OneDriver(Browsers.Chrome);
browser.GoToUrl(link);
browser.FindElementById("txtUserName").SendKeys(username);
browser.FindElementById("txtpassword").SendKeys(password);
// you probably want to click on something here
}
// some other test
private void ClickCart()
{
browser.FindElementById("btnCart").Click();
}
// add other actions here
// this starts the test for ONE browser
public static void Start(string user, string pwd)
{
var runner = new BrowserTestRunner();
runner.OpenBrowser(user, pwd);
// wait for stuff, check data, prepare the next steps
// for example
// runner.ClickCart();
// other actons here
}
}
Now you can create as many Test class instances as you like, while each instance of the class manages its own internal state, without interfering with other instances:
Parallel.ForEach(dtUser.AsEnumerable(), items =>
BrowserTestRunner.Start(items["user"].ToString(), items["pass"].ToString()));
If you want to start that from your backgroundworker do:
private void AllCaseProgram(object args)
{
try
{
switch (numProcess)
{
case 0:
Parallel.ForEach(
dtUser.AsEnumerable(),
items => BrowserTestRunner.Start(items["user"].ToString(), items["pass"].ToString()));
break;
case 1:
ClickCart();
break;
case 2:
Result();
break;
}
}
catch (Exception ex)
{
if (browser != null)
browser.Cleanup();
numProcess = 0;
AllCaseProgram(null);
}
}
By all means: don't start the main form again. Just separate your WinForm from the code you use to operate the browser. That does mean that you have to move the code that interacts with the browser to the BrowserTestRunner. Don't try in keeping the logic for your selenium stuff in the WinForm class because that is doomed to fail. As you are already experiencing.
What you got here is sort of a race condition. You got two threads not getting along when handling a single field in the class. Your problem is only that you don't have sufficient space to store all the browser instances you require.
What happens is basically that the first thread enters the method, creates a instance of the chrome browser and stores it in the variable. Then the second thread enters the function and does the same thing. But it also stores the instance in the same variable. Now the first thread continues and goes to a link. But the instance it is working with is already replaced by the second thread. And so on. This may happen with the threads the other way around or the overlapping may happen after more lines where handled. But it is bound to go wrong.
The way to resolve it, is as you noticed to make the variable local by adding a var. This way both threads are working with distinct variables.
Now you said you need the variable in another function. The question is: Do you need both? Do you need only one? Do you need a specific one?
In case you need only one, you just store the variable in the global variable by adding a line like this in your function:
this.browser = browser;
So it would look like this in total:
Lapsoft_OneDriver browser;
public void OpenBrowser(string username, string password)
{
var localBrowser = new Lapsoft_OneDriver(Browsers.Chrome);
localBrowser.GoToUrl(link);
localBrowser.FindElementById("txtUserName").SendKeys(username);
localBrowser.FindElementById("txtpassword").SendKeys(password);
this.browser = localBrowser;
}
I changed the name of the local browser variable, so it gets clearer what variable is used. Do note that either one of the created browsers could end up in the variable.
In case you need a specific one you have to determine if you have the correct one and store the result after this.
If you need both you have to store them in a list. The namespace System.Collections.Concurrent offers lists that can be handled by multiple threads at once.

Calling a function within a listener function

I'm new to the C# world and I am trying to call another function inside a listener using this code below:
private void Form1_Load(object sender, EventArgs e)
{
listener = new GestureListener(100);
listener.onGesture += listener_onGesture;
controller = new Controller(listener);
}
static void listener_onGesture(Gesture gesture)
{
string gestures = "";
foreach (Gesture.Direction direction in gesture.directions) {
gestures = direction.ToString();
}
int howManyFingers = gesture.fingers;
if (gestures == "Left" && howManyFingers == 2) {
test();
} else {
Console.WriteLine("gestured " + gestures + " with " + gesture.fingers + " fingers.");
}
}
private void test()
{
pdf.gotoNextPage();
}
However, it does not seem to work when i do that. The error it gives me on the line test(); is:
An object reference is required for the non-static field, method, or property 'LeapDemoTest.Form1.test()'
How can i do this?
You're seeing this because listener_onGesture is a static method -- meaning, the method is not associated with a given instance of your class. However, test is an instance method -- so it is scoped to the specific instance.
I see three options, depending on the scope of "pdf", but I recommend option 1:
Make listener_onGesture an instance method (remove the static keyword)
Make test a static method -- this will only work if pdf is also a static member.
Somewhat hackish -- find the Form instance that invoked the event by inspecting the sender's properties and invoke the test method on that instance.
listener_onGesture probably shouldn't be static. You want to access instance fields within this method, and you appear to be calling it from within an instance of the application (Form1_Load, where you currently reference it from, is not a static method). By removing the static modifier from that method you will then be able to call a non-static method.

Providing multiple instances of a form yet processing events one at a time

I need to be able to let multiple instances of the same form be open as my application can be used in different places at once. On the other hand I need to be able to process the operations during the "OK" event one at a time to ensure data is stored safely and not overwritten by another form instance by accident.
I show my form using the .Show() method as I am using a few delegates in it:
private void newToolStripMenuItem_Click(object sender, EventArgs e)
{
bookingForm = new BookingForm(AddMemberBooking, AddUserBooking, CloseBooking);
bookingForm.Show();
}
I have tried to use the mutex to allow only one event of the OK button being pressed happen at a time, i have combined this with a Thread to meet the criteria i need.
When i click on the "OK" button I am given the following error:
Cross-thread operation not valid: Control 'comboBoxDay' accessed from a thread other than the thread it was created on.
This is the code for my booking form class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace Collection
{
//Allows the class to be serialized
[Serializable()]
public delegate void AddMemberBookingMethod(int date, int time, int mNo);
public delegate void AddUserBookingMethod(int date, int time, string fName, string lName, string pCode);
public delegate void CloseBookingFormMethod();
public partial class BookingForm : Form
{
public CloseBookingFormMethod CloseBookingForm;
public AddMemberBookingMethod AddMemberBooking;
public AddUserBookingMethod AddUserBooking;
private Mutex bookingMut = new Mutex();
private Thread thread;
public bool IsUser;
public BookingForm(AddMemberBookingMethod ambm, AddUserBookingMethod aubm, CloseBookingFormMethod cbfm)
{
InitializeComponent();
AddMemberBooking = ambm;
AddUserBooking = aubm;
CloseBookingForm = cbfm;
checkBoxMember.Checked = true;
//Control.CheckForIllegalCrossThreadCalls = false;
}
private void checkBoxUser_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxUser.Checked)
{
IsUser = true;
checkBoxMember.CheckState = CheckState.Unchecked;
textBoxMNo.Enabled = false;
textBoxFName.Enabled = true;
textBoxLName.Enabled = true;
textBoxPCode.Enabled = true;
}
else
{
IsUser = false;
checkBoxMember.CheckState = CheckState.Checked;
textBoxMNo.Enabled = true;
textBoxFName.Enabled = false;
textBoxLName.Enabled = false;
textBoxPCode.Enabled = false;
}
}
private void checkBoxMember_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxMember.Checked)
{
IsUser = false;
checkBoxUser.CheckState = CheckState.Unchecked;
textBoxFName.Enabled = false;
textBoxLName.Enabled = false;
textBoxPCode.Enabled = false;
}
else
{
IsUser = true;
checkBoxUser.CheckState = CheckState.Checked;
textBoxMNo.Enabled = false;
textBoxFName.Enabled = true;
textBoxLName.Enabled = true;
textBoxPCode.Enabled = true;
}
}
private void buttonOK_Click(object sender, EventArgs e)
{
this.thread = new Thread(new ThreadStart(MakeBooking));
this.thread.Name = "bookingThread";
this.thread.Start();
}
private void MakeBooking()
{
this.bookingMut.WaitOne();
int date = this.comboBoxDay.SelectedIndex;
int time = this.comboBoxTime.SelectedIndex;
if (IsUser)
{
string fName = textBoxFName.Text;
string lName = textBoxLName.Text;
string pCode = textBoxPCode.Text;
AddUserBooking(date, time, fName, lName, pCode);
}
else
{
int mNo = int.Parse(textBoxMNo.Text);
AddMemberBooking(date, time, mNo);
}
this.bookingMut.ReleaseMutex();
CloseBookingForm();
}
private void buttonClose_Click(object sender, EventArgs e)
{
CloseBookingForm();
}
}
}
I realise I may not be doing this in the most efficient way but time is a bit of a factor.
I've researched the error and have heard of using delegates and .Invoke() but I'm still not entirely sure how to fix it.
EDIT:
I've found this code snippet when searching for a fix to my problem. I don't understand where/how I would use it.
if(this.InvokeRequired)
{
this.Invoke(new MyEventHandler(this.CreateAForm()));
return;
}
EDIT2:
Seems the guy finally saw sense, by creating the from with the new word it apparently passes the criteria. I wish I'd have known this before trying to reinvent the wheel.
You are getting this exception because your thread is accessing controls. That's not legal, control properties must only ever be accessed from the UI thread. You're okay on the TextBox.Text property, that one happens to be cached. But not ComboBox.SelectedIndex. And closing the form from another thread is going to bomb too.
Your mutex has nothing to do with it, but keep it if you want to prevent threads from overlapping. Using a delegate's Invoke method isn't going to solve it, that just starts a thread as well. You'll need to collect the info that the thread is going to need in a little helper class and pass that as the argument to the Thread.Start() method.
Closing the form is a bit tricky too, the user might well have already closed it while the thread was running. That's going to cause an ObjectDisposed exception. A quick fix is to set the form's Enabled property to false so the user can't close it. You'll need to use the form's Invoke() method to ensure the closing is done on the UI thread.
Last but not least, if these threads don't take a lot of time (a second or so), consider not using threads at all and display a wait cursor instead.
One simple way to do this is to use the overload of the Thread.Start method that accepts an object: Thread.Start Method (Object). In this object you will store all the data/state necessary in order to make the update.
All the code that references the form and its controls needs to be moved into the OK click event method or refactored out to a method that just returns a data object. Then pass this object into the thread start method.
Some pseudo code:
on_click_event()
{
object data=getFormData();
thread.start(data);
}
There are better ways to do this but this is a quick fix for your code.
I think you could simply disable the OK buttons on other open forms to give users a visual cue. Then you shouldn't even have the issue. Provide a callback delegate to something in the application controller which knows which forms are open. Each form can provide a public method to disable the OK button. Disable to OK button on all the other forms.
I'm not really following your code too well. I would think the mutex could be outside of the form code in the first place (i.e. in the delegates that do the actual work), and if it is within a single application, you could just use the lock (object) method to ensure only one thread is executing a given bit of code.
I'd also like to add that a mutex is not going to stop multiple users on different machiens being able to click OK at the same time. I'm not sure if that's what you meant in your question by a form being run in different places.
I think that AddUserBooking and the other delegate should be responsible for ensuring that they are threadsafe and this should not be part of the UI. If they aren't threadsafe, why aren't they? It's relatively easy to make database commit functions each have their own connection to the database during their operations and thread-safety should not be an issue.

Categories

Resources