I have winform application where i am trying to play multiple videos and i am creating threading for that. My code is :
public String[,] vpath = new String[3, 7];
public Video[,] video = new Video[3, 7];
public static Thread[,] th = new Thread[3, 7];
public void playclick(object sender, EventArgs e)
{
int i, j;
for (j = 0; j <= 7 - 1; j++)
{
for (i = 0; i <= 3 - 1; i++)
{
if (btnp[i, j].Capture)
{
//play();
th[i, j] = new Thread(new ThreadStart(play));
th[i, j].IsBackground = true;
th[i, j].Start();
}
}
}
}
public void play()
{
int i, j;
for (j = 0; j <= 7 - 1; j++)
{
for (i = 0; i <= 3 - 1; i++)
{
if (th[i, j].ThreadState == ThreadState.Running) // Having problem here
{
if (video[i, j].State != StateFlags.Running)
{
video[i, j].Play();
}
}
}
}
}
So with out that if statement it will run all the videos on single button press. But i want to run the particular video which the thread is in ..
pls help me guys
ThreadState is a bitmask-type property (enum has the [Flags] property, that's always the hint), so you don't check it directly using ==, you only need to check the relevant bit:
if ((t.ThreadState & ThreadState.Running) == ThreadState.Running) { ...
Read here about the meanings of the ThreadState values. From reading that and possibly the whole article, or whole book (highly recommended!) you'll also most likely notice that yours is probably not the ideal approach.
Not knowing your exact endgame though, it;s hard to suggest an exact one.
As to why you are getting an exception, HaemEternal nailed that in his comment. You are only initializing one thread at a time, yet you are checking all of them. A null thread object does not have a ThreadState value.
May I suggest though, that you change your design altogether;
There is no need to constantly check which thread was activated. You can change the signature of the Play() method to accept an Object, and you can pass the correct video to the method using that Object.
public void playclick(object sender, EventArgs e)
{
int i, j;
for (j = 0; j <= 7 - 1; j++)
{
for (i = 0; i <= 3 - 1; i++)
{
if (btnp[i, j].Capture)
{
//play();
th[i, j] = new Thread(new ParameterizedThreadStart(play));
th[i, j].IsBackground = true;
th[i, j].Start(video[i,j]);
}
}
}
}
public void play(object video)
{
Video vid = video as Video;
if (vid.State != StateFlags.Running)
{
vid.Play();
}
}
An even better approach is to encapsulate these three elements in a single object that contains a Video object, a Thread object, and a path string.
If you own the Video class, you might even want to make the Thread and the string values fields of that class.
You might even want to create a field on your buttons of type of this new object, so each button will be associated with a button.
This is much more typical of object oriented design. There is no reason you should maintain four separate identically sized arrays, each of different type.
The answer by #tar gives some hints but the code is wrong (as commented by #Sampath).
This come from the fact that ThreadState is implemented in a questionable strange way:
Normally a bitmask state is implemented using for example bit 1 for state on and the
same bit for the opposite off. This is not the case, in fact, for example, the Running
state has a 0 value, whereas the 1 value is taken by StopRequested.
So it is not wise to do a bit check.
A first approach would be to check for state with an or statement:
while (t.ThreadState == ThreadState.Running ||
t.ThreadState == ThreadState.Background)
Application.DoEvents();
t.Join();
Keep in mind that if you start a process in background you will have the
ThreadState.Background enum value returned and not ThreadState.Running,
this is why I have put both.
The better and simpler approach is:
while (t.IsAlive)
Application.DoEvents();
t.Join();
if (th.ThreadState.Equals(ThreadState.Unstarted))
th.Start();
Related
When I do this for loop on a windows form app and I click a button to start this for loop, the label just changes to the last item in the array, how do I get it to go through the array one at a time?
for (int i = 0; i < WordsnQuestions.questions.Length; i++)
{
lblScrambled.Text = WordsnQuestions.questions [i++];
}
The loop is too fast... the label changes for every item in the loop (note: loop, not array, because of another issue), but the form doesn't have a chance to repaint anything until after the loop is already finished. By this time we're already up to the last item, so that's what we see.
The typical fix for this is move the index outside of the method, and only show one item for each event:
int currentPosition = 0;
public void btnNext_Click(object sender, EventArgs e)
{
lblScrambled.Text = WordsnQuestions.questions [currentPosition++];
if (currentPosition >= WordsnQuestions.questions.Length) currentPosition = 0;
}
Separate from this, the original code incremented the counter twice per loop iteration... but again, properly designing the interaction avoids this in the first place.
But let's say you wanted an animation effect. Then you might try something like this to slow things down (note only one increment operator is used):
for (int i = 0; i < WordsnQuestions.questions.Length; i++)
{
lblScrambled.Text = WordsnQuestions.questions [i];
Thread.Sleep(1000);
}
But this still won't work, because the code still does not yield control back to the form so it can repaint the window.
Now you might be tempted to do something like this:
for (int i = 0; i < WordsnQuestions.questions.Length; i++)
{
lblScrambled.Text = WordsnQuestions.questions [i];
Application.DoEvents();
Thread.Sleep(1000);
}
This will seem to (finally!) work, because it does allow an event loop to finally process each of WM_PAINT messages. However, there are still all kinds of problems with it. Again, the correct solution is really to think more carefully about how the interaction should work in the first place.
There are two issues here:
i++ is twice so your skipping items #2, 4, 6... and looping over 1, 3, 5 ... etc
lblScrambled.Text will have the last item and program will not fail if you have odd number of items.
Here is the updated code for you to try:
for (int i = 0; i < WordsnQuestions.questions.Length; i++)
{
lblScrambled.Text += WordsnQuestions.questions [i];
}
I'm on a project coding a 4 axes robot arm.
I'm working on Virtual Studio and coding in C#. I'm NEW to C# so please be tolerant.
The robot came with a large .cs library and a Programming Manual with sample methods.
The sample interface :
sample_interface
I used cetains of these samples to build an operation method which makes the arm accomplishes repetitive movements for a chosen number of times. (I erased some movements here to avoid too much useless code) Here it comes :
private async void button_operation_Click(object sender, EventArgs e)
{
int op_number;
for (op_number = 0; op_number < Convert.ToInt32(textBox_operation.Text); op_number++) {
int i;
for (i = 0; i < 4; i++)
{
axis_list[i] = i;
}
v_dist[0] = 60;
v_dist[1] = 10000;
v_dist[2] = -1000;
v_dist[3] = -8000;
if ((int)g_handle != 0)
{
zmcaux.ZAux_Direct_Base(g_handle, 4, axis_list);
zmcaux.ZAux_Direct_SetSpeed(g_handle, axis_list[0], 10000);
zmcaux.ZAux_Direct_MoveAbs(g_handle, 4, v_dist);
zmcaux.ZAux_Direct_GetIfIdle(g_handle, 3, ref idle_state[3]);
make_pause(g_handle, 3);
v_dist[0] = 50;
v_dist[2] = -1700;
v_dist[3] = -10550;
zmcaux.ZAux_Direct_MoveAbs(g_handle, 4, v_dist);
zmcaux.ZAux_Direct_GetIfIdle(g_handle, 3, ref idle_state[3]);
make_pause(g_handle, 3);
v_dist[0] = 60;
v_dist[2] = -1000;
v_dist[3] = -8000;
zmcaux.ZAux_Direct_MoveAbs(g_handle, 4, v_dist);
zmcaux.ZAux_Direct_GetIfIdle(g_handle, 3, ref idle_state[3]);
make_pause(g_handle, 3);
}
}
}
The make_pause() method is to make the robot wait the end of a movement before beggining the next :
{
while (idle_state[iaxis] == 0)
{
zmcaux.ZAux_Direct_GetIfIdle(g_handle, iaxis, ref idle_state[iaxis]);
}
}
By side, I also used another sample to add a "STOP" button but for this one, I just had to copy the whole method as it comes. Its role is to obviously cancel every movement. However, it's not working.
The sample method I used for the Stop Button :
{
if ((int)g_handle != 0)
{
zmcaux.ZAux_Direct_Rapidstop(g_handle,2);
}
}
My first though is that my operation method construction does not allow another method call to cancel.
Thus, I made a TEST button to create a single movement just like on my sample project.
Problem is that the STOP button is still not working.
What am I missing ? Is there something wrong with the way I am building the methods or project ?
Thanks
I have an automatic function, the idea is to execute in an specific time some actions.
It receives a ListView with the actions and specific times.
It runs almost pefect. I say almost because sometimes it doesn't executes one action (lets say action #10, and it has 30 actions to do) and when this happen the rest of the actions neither get executed.
I have some validations where I check if the previous actions is executed then the current one is excuted, but it doesn't make any difference, it continues stopping at some point.
Here the code for this automatic function:
Constructor
public RunAutomatic()
{
InitializeComponent();
stopwatch = new System.Windows.Forms.Timer();
stopwatch.Interval = 5;
stopwatch.Tick += new EventHandler(stopwatch_Tick);
repSeconds = 0;
for (int x = 0; x < repeatActions.Capacity; x++)
{
repeatActions.Add(x);
finished.Add(x);
}
}
Run Function
private void RunFaster()
{
if (contPos > 1)
{
ArrayList tmp = (ArrayList)repeatActions[contPos - 1];
if ((bool)tmp[tmp.Count - 1] == true) //This is where it is supposed to validate that the previous action is already executed
Execute(2);
else
{
contPos = contPos - 1;
Execute(2);
}
}
else
Execute(2);
}
I have tried to solve this but can't get over it.
Thanks to all replies.
When using locks, do I need to lock around my get? From my testing I do not need to do so, but I wanted to make sure. Also, how do I format the code I posted so it has the proper schema colors? It is asking me to add more details, but I don't really know what to add- I am moreover asking (from someone more experienced than I) if what I have is correct and will work without throwing cross thread exceptions.
class exam
{
private static readonly exam x = new exam();
private static readonly object lckobj = new object();
private int i;
private int _count;
private exam() { }
public static exam AccessPoint
{
get
{
return x;
}
}
public int myInt
{
get
{
return i;
}
set
{
lock(lckobj)
{
i = value;
}
}
}
public int Count
{
get
{
return _count;
}
set
{
lock(lckobj)
{
_count = value;
}
}
}
}
class myDemo
{
Random r = new Random();
bool b = false;
Thread[] t = new Thread[3];
public myDemo()
{
for(int i=0; i < 3; i++)
{
t[i] = new Thread(new ThreadStart(thread1));
t[i].Start();
}
Thread checks = new Thread(new ThreadStart(checkB));
checks.Start();
}
void checkB()
{
var x = exam.AccessPoint;
while (!b)
{
b = (x.Count >= 10) ? true : false;
Console.WriteLine("\tb:{0}\tCount:{1}", b, x.Count);
Thread.Sleep(100);
}
}
void thread1()
{
var x = exam.AccessPoint;
while (!b)
{
Thread.Sleep(r.Next(500, 1000));
x.myInt = r.Next(1, 10);
x.Count = x.Count + 1;
Console.WriteLine(x.myInt);
}
}
}
Even if you added the lock around the get your code still wouldn't work properly, although there are more possible things that can go wrong if you don't do that.
The following line is problematic in a way that cannot be fixed by locking in Count:
x.Count = x.Count + 1;
Here even if you add locks, it's entirely possible for one thread to read a value, stop before updating, then have another thread read the value, increment the count, and then write it back. That write will be overridden when the first thread continues on. No amount of locking in Count will change that.
Of course, without the lock in Count there is no memory barrier introduced, so reads to that value are allowed to be reading stale values, which can further exacerbate the previous problem.
Reading a value of a variable by multiple threads in the same time is safe.
Writing a value to a variable by multiple threads in the same time is not safe.
Writing a value to a variable by one thread in the same time as one ore more threads are reading from this variable is not safe.
So using the same lock in both setter and getter is required.
Problem described in #Servy answer requires a better solution though. Any flavour of locking mechanism which wraps so called "SELECT FOR UPDATE" (I know this goes for DB but the problem is the same) should be good enough.
You should even reuse the same lock you already have in your code. Make it public since it's readonly and for such situations use it from outside:
lock(exam.lckobj){
exam.myInt = exam.myInt + 1;
}
For integer values use Interlocked.Read and Interlock.Exchange. Very atomic, very thread safe, doesn't carry the weight of a mutex (via lock).
I've got a problem with adding Expanded event to my Expanders. I have expanders on my Window and I want to get the effect when I expand my expander all other will go down. I write functions that let me do this and it work correct. The problem is that I have 96 expanders I don't want add 96 events for Expand and 96 events for Collapse so I thought that I can add this programmatically.
look at the code:
private void InitExpanders()
{
var expanders = GetExpanders(); // List<Expander> - list of expanders
for (int i = 0; i < expanders.Count; i++)
{
if (i % 6 == 1)
{
expanders[i - 1].Expanded += new RoutedEventHandler(delegate(object sender, RoutedEventArgs args)
{
DisableBigExpanders(1); // problem is here!
});
}
}
}
this code works fine but for each expander function parameter will be 1.
Ive tried to add integer and increment it but it wont works.
private void InitExpanders()
{
var expanders = GetExpanders();
int x = 0;
for (int i = 0; i < expanders.Count; i++)
{
if (i % 6 == 1)
{
expanders[i - 1].Expanded += new RoutedEventHandler(delegate(object sender, RoutedEventArgs args)
{
DisableBigExpanders(x);
});
x++;
}
}
}
Thanks for all replies.
I suspect you are finding x in the delegate is always the highest value it reached during the loop. This is due to the way the compiler instantiates the instance of the anonymous method you've defined. It looks at what captured outer variables there are around the delegate and decides whether it requires separate instances or if it can use a single instance. In this case, you have no captured outer variables; therefore, the compiler is permitted to use the same instance of the delegate and therefore the same value of x.
To get around this issue, all you need to do is add a more granular variable just before the delegate and assign it the value of x. This will work:
private void InitExpanders()
{
var expanders = GetExpanders();
int x = 0;
for (int i = 0; i < expanders.Count; i++)
{
if (i % 6 == 1)
{
int y = x++;
expanders[i - 1].Expanded += delegate
{
DisableBigExpanders(y);
};
}
}
}
See here for more info on the theory: http://en.csharp-online.net/ECMA-334%3A_14.5.15.4_Anonymous_method_evaluation