Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Mutitasking help needed


  • Please log in to reply
12 replies to this topic

#1 perkunas

perkunas

    Advanced Member

  • Members
  • PipPipPip
  • 108 posts

Posted 06 April 2012 - 01:39 PM

This is my 1st netduino project, and I never could of got this far without some wonderful and talented people helping me.
Anyway....
It wont work; well at least not the way I expected.
I want the treads to go in order, but sadly it seems, they are all trying to fire at the same time.
I want this
thread 1 fires finishes
thread 2,3,4 ,5 fires finishes joined
thread 6,7 fires finishes joined
thread 8 fires finishes joined to 1
loop
using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;


namespace controller
{
public class Program
{
public const int SecondMs = 1000;
public const int MinuteMs = 60 * SecondMs;
public const int HourMs = 60 * MinuteMs;
public const int DayMs = 24 * HourMs;
public const int WeekMs = 7 * DayMs;

static OutputPort ph = new OutputPort(Pins.GPIO_PIN_D2, true);
static OutputPort b = new OutputPort(Pins.GPIO_PIN_D3, true);
static OutputPort a = new OutputPort(Pins.GPIO_PIN_D4, true);
static OutputPort drain = new OutputPort(Pins.GPIO_PIN_D5, true);
static OutputPort solenoid = new OutputPort(Pins.GPIO_PIN_D6, true);
static OutputPort heater = new OutputPort(Pins.GPIO_PIN_D7, true);
static OutputPort controller = new OutputPort(Pins.GPIO_PIN_D8, true);

public static void Main()
{
Thread tid_1 = new Thread(new ThreadStart(Thread_1));
Thread tid_2 = new Thread(new ThreadStart(Thread_2));
Thread tid_3 = new Thread(new ThreadStart(Thread_3));
Thread tid_4 = new Thread(new ThreadStart(Thread_4));
Thread tid_5 = new Thread(new ThreadStart(Thread_5));
Thread tid_6 = new Thread(new ThreadStart(Thread_6));
Thread tid_7 = new Thread(new ThreadStart(Thread_7));
Thread tid_8 = new Thread(new ThreadStart(Thread_8));

while (true)
{

tid_1.Start();

tid_2.Start();
tid_3.Start();
tid_4.Start();
tid_5.Start();

tid_2.Join();
tid_3.Join();
tid_4.Join();
tid_5.Join();

tid_6.Start();
tid_7.Start();

tid_6.Join();
tid_7.Join();
tid_6.Abort();

tid_8.Start();
tid_1.Join();

}
}

public static void Thread_1()
{

drain.Write(false);
Thread.Sleep(16 * MinuteMs);
drain.Write(true);

}

public static void Thread_2()
{
solenoid.Write(false);
Thread.Sleep(16 * MinuteMs);
solenoid.Write(true);
}

public static void Thread_3()
{
a.Write(false);
Thread.Sleep(11 * MinuteMs + 7 * SecondMs);
a.Write(true);
}

public static void Thread_4()
{
b.Write(false);
Thread.Sleep(6 * MinuteMs + 3 * SecondMs);
b.Write(true);
}

public static void Thread_5()
{
ph.Write(false);
Thread.Sleep(3* SecondMs);
ph.Write(true);
}

public static void Thread_6()
{
while (true)
{
controller.Write(false);
Thread.Sleep(1 * SecondMs);
controller.Write(true);
Thread.Sleep(10 * MinuteMs);
}
}
public static void Thread_7()
{
heater.Write(false);
for (int i = 0; i < 14; i++)
{
solenoid.Write(false);
Thread.Sleep(20 * SecondMs);
solenoid.Write(true);
Thread.Sleep(1 * DayMs);

}
heater.Write(true); // added this
}

public static void Thread_8()
{
drain.Write(false);
Thread.Sleep(16 * MinuteMs);
solenoid.Write(false);
Thread.Sleep(16 * MinuteMs);
Thread.Sleep(30 * MinuteMs);
}
}
}

Edited by Chris Walker, 12 April 2012 - 12:05 AM.
added [code][/code] tags


#2 CW2

CW2

    Advanced Member

  • Members
  • PipPipPip
  • 1592 posts
  • LocationCzech Republic

Posted 06 April 2012 - 01:49 PM

I have been following the other thread for a while and then got lost completely. IMVHO what you'd probably need is to write down a table with the time periods required by the signals (for us to understand the requirements) and then simply use Timer-s... Posted Image

#3 perkunas

perkunas

    Advanced Member

  • Members
  • PipPipPip
  • 108 posts

Posted 06 April 2012 - 02:14 PM

Not sure I follow the timer thing, looking for a logical sequence of execution. why is something not joined firing at the same time with things joined to something else. Maybe that's how it operates all the threads fire same time IDK. I made tons of confusing changes, over time, so I thought it might be good to start a fresh post not to confuse people even more.

#4 CW2

CW2

    Advanced Member

  • Members
  • PipPipPip
  • 1592 posts
  • LocationCzech Republic

Posted 06 April 2012 - 03:04 PM

Not sure I follow the timer thing, looking for a logical sequence of execution.

What I can see in the code, you need the signals to change at defined times, a timer is like thread which can be scheduled to fire at certain time, basically replacing 'sleep' in the thread. However, using a different approach, would the following simple sequence meet your requirements?

// Thread_1, 2 and 3
drain.Write(false);
solenoid.Write(false);
a.Write(false);
Thread.Sleep(11*MinuteMs + 7*SecondMs); // Thread_3
a.Write(true);
// Wait 16 minutes from the beginning, but 11 minutes 7 seconds already elapsed,
// so sleep for 4 minutes 53 seconds
Thread.Sleep(4*MinuteMs + 53*SecondMs);
drain.Write(true);
solenoid.Write(true);


#5 ErikN

ErikN

    Advanced Member

  • Members
  • PipPipPip
  • 119 posts
  • LocationNew York, NY

Posted 06 April 2012 - 03:19 PM

I'm a little confused on what you're trying to accomplish (I don't know the other thread to which CW2 referred).
Looking at your post, this is what I understand you're asking for - please correct me if this is wrong.


[==T1==>]
        [==T2==>]
        [==T3=>]|
        [==T4=>]|
        [=T5=>] V
                [==T6=>]
                [========T7=======>]
                                   [====T8====>]

Or is this more accurate?


[==================T1==================>]
 [==T2==>]                              |
 [==T3=>]|                              |
 [==T4=>]|                              |
 [=T5=>] V                              | 
         [==T6=>]                       |
         [========T7=======>]           V
                            [====T8====>]



#6 ErikN

ErikN

    Advanced Member

  • Members
  • PipPipPip
  • 119 posts
  • LocationNew York, NY

Posted 06 April 2012 - 03:49 PM

If your methods are timed correctly and you don't want to adjust your logic, I'd just create a helper function that takes an array of delegates (VoidAction delegate?)
and runs them each in a new thread using a ManualResetEvent and a counter to track when each finish and only mre.Set when they've all completed. This would
make your helper function become a blocking call which starts and tracks the completion of multiple threads at once.

public delegate void VoidAction();
void AwaitCompletionOf(VoidAction[] actions);


For the first timing diagram, your code would be as simple as the pseudo-code below:

loop:
Thread_1(); // This is call to method directly so it blocks
AwaitCompletionOf(new VoidAction[] {Thread_2, Thread_3, Thread_4, Thread_5}); //Executing parallel; group blocks
AwaitCompletionOf(new VoidAction[] {Thread_6, Thread_7}); //Same as previous
Thread_8();

The second timing diagram would require a bit more work to synchronize the 1st and 8th methods while running the middle threads in waiting parallel groups.

For inspiration, you can look at the beta (but works!) version of a ParallelForEach extension I'd written for NETMF awhile back. It was refined with feedback from Corey Kosak here on the Netduino boards.

#7 CW2

CW2

    Advanced Member

  • Members
  • PipPipPip
  • 1592 posts
  • LocationCzech Republic

Posted 06 April 2012 - 04:43 PM

(I don't know the other thread to which CW2 referred).

Just for clarification, it's simple code need help topic.

#8 ErikN

ErikN

    Advanced Member

  • Members
  • PipPipPip
  • 119 posts
  • LocationNew York, NY

Posted 06 April 2012 - 07:29 PM

Some good approaches and advice in that very long thread. As soon as my eyes uncross I could try giving it another pass. Posted Image

#9 perkunas

perkunas

    Advanced Member

  • Members
  • PipPipPip
  • 108 posts

Posted 07 April 2012 - 02:07 PM

ya that's it 1st one [==T1==>] [==T2==>] [==T3=>]| [==T4=>]| [=T5=>] V [==T6=>] [========T7=======>] [====T8====>] and I think that's the answer too tnx loop: Thread_1(); // This is call to method directly so it blocks AwaitCompletionOf(new VoidAction[] {Thread_2, Thread_3, Thread_4, Thread_5}); //Executing parallel; group blocks AwaitCompletionOf(new VoidAction[] {Thread_6, Thread_7}); //Same as previous ....but I got to go to work right now, and don't have time to try it, or put or together I'll do it later. The times are going to be variable so the basic thing no thread ie. no multitask wont work // Thread_1, 2 and 3 drain.Write(false); solenoid.Write(false); a.Write(false); Thread.Sleep(11*MinuteMs + 7*SecondMs); // Thread_3 a.Write(true); // Wait 16 minutes from the beginning, but 11 minutes 7 seconds already elapsed, // so sleep for 4 minutes 53 seconds Thread.Sleep(4*MinuteMs + 53*SecondMs); drain.Write(true); solenoid.Write(true); Thread_8(); P.S. I love how it says Advanced Member next to my name, this is my 1st project lol Something like that could go to your head

#10 perkunas

perkunas

    Advanced Member

  • Members
  • PipPipPip
  • 108 posts

Posted 07 April 2012 - 11:08 PM

well I have the answer here with the... loop: Thread_1(); // This is call to method directly so it blocks AwaitCompletionOf(new VoidAction[] {Thread_2, Thread_3, Thread_4, Thread_5}); //Executing parallel; group blocks AwaitCompletionOf(new VoidAction[] {Thread_6, Thread_7}); //Same as previous Thread_8(); But I'm struggling with trying to put it together, error this and that. can I get a little more help please. Tnx

#11 peterfjorgensen

peterfjorgensen

    New Member

  • Members
  • Pip
  • 9 posts
  • LocationDenmark

Posted 11 April 2012 - 01:53 PM

Hi,

I have looked at your code and basically it is a sound way of doing what you are trying to do :) .
There are a few mistakes which prevents it from working, though.

Also I'm not completely sure of your sequence, but it is easy to add the appropriate Join call either after the start of the tid_1.Start() or after the tid_8.Start(), whatever you need.

1. The main problem is that thread tid_6 will run forever (because of the "while(true)" in the thread) and prevent the whole sequence from finishing. Thread tid_6 will run forever because you have a misplaced "tid_6.Join()" before you have a call to "tid_6.Abort()".
When making a call to the Abort() method of the thread, a ThreadAbortException will be thrown which you will have to deal with (see my code below). I don't really do something sensible here, but it is just to show the construction...

2. You are not waiting to let tid_8 finish before you try to run the whole sequence again. You should add a call to "tid_8.Join()".

3. To restart all threads after the full sequence has been running through one full cycle (all 8 threads finished), you need to reinitialize all 8 threads. This has to be done inside your main "while(true)" loop.

When debugging code (especially with threads) it is always a good idea to add "Debug.Print" statements in appropriate places. That way you can always see when threads start and finish as expected.

The way you have implemented your timing is good as it is easy to "speed up" the execution during testing :)

I have attached your code with my (small) modifications and you should be able to have this up and running just by copy-paste.

Good luck.
Peter


// The code implements execution of threads in the following sequence:
// [==T1==>]
//          [==T2==>]
//          [==T3=>]
//          [==T4=>]
//          [=T5=>] 
//                   [========T6=========]
//                   [========T7=======>]
//                                        [====T8====>]


public static void Main()
{
    Thread tid_1;
    Thread tid_2;
    Thread tid_3;
    Thread tid_4;
    Thread tid_5;
    Thread tid_6;
    Thread tid_7;
    Thread tid_8;

    while (true)
    {
        // (Re)Initialize all 8 threads
        tid_1 = new Thread(new ThreadStart(Thread_1));
        tid_2 = new Thread(new ThreadStart(Thread_2));
        tid_3 = new Thread(new ThreadStart(Thread_3));
        tid_4 = new Thread(new ThreadStart(Thread_4));
        tid_5 = new Thread(new ThreadStart(Thread_5));
        tid_6 = new Thread(new ThreadStart(Thread_6));
        tid_7 = new Thread(new ThreadStart(Thread_7));
        tid_8 = new Thread(new ThreadStart(Thread_8));
                
        tid_1.Start();
        tid_1.Join(); // Peter added this. This line makes sure that tid_1 is finished before starting tid_2..tid_5

        tid_2.Start();
        tid_3.Start();
        tid_4.Start();
        tid_5.Start();

        tid_2.Join();
        tid_3.Join();
        tid_4.Join();
        tid_5.Join();

        tid_6.Start();
        tid_7.Start();

        //tid_6.Join(); // Peter: Remove this as the code will never come here because of the while(true) inside the tid_6 thread.
        tid_7.Join();

        tid_6.Abort(); // This will force the tid_6 thread to terminate and throw a ThreadAbortException which you have to deal with...
        tid_6.Join(); // Peter added this

        tid_8.Start();
        tid_8.Join(); // Peter added this
    }
}

public static void Thread_1()
{
    Debug.Print("1. Start");

    drain.Write(false);
    Thread.Sleep(16 * MinuteMs);
    drain.Write(true);

    Debug.Print("1. End");
}

public static void Thread_2()
{
    Debug.Print("2. Start");

    solenoid.Write(false);
    Thread.Sleep(16 * MinuteMs);
    solenoid.Write(true);

    Debug.Print("2. End");
}

public static void Thread_3()
{
    Debug.Print("3. Start");

    a.Write(false);
    Thread.Sleep(11 * MinuteMs + 7 * SecondMs);
    a.Write(true);

    Debug.Print("3. End");
}

public static void Thread_4()
{
    Debug.Print("4. Start");

    b.Write(false);
    Thread.Sleep(6 * MinuteMs + 3 * SecondMs);
    b.Write(true);

    Debug.Print("4. End");
}

public static void Thread_5()
{
    Debug.Print("5. Start");

    ph.Write(false);
    Thread.Sleep(3 * SecondMs);
    ph.Write(true);

    Debug.Print("5. End");
}

public static void Thread_6()
{
    Debug.Print("6. Start");

    bool running = true;
    try
    {
        while (running)
        {
            controller.Write(false);
            Thread.Sleep(1 * SecondMs);
            controller.Write(true);
            Thread.Sleep(10 * MinuteMs);

            Debug.Print("6. Running");
        }
    }
    catch (ThreadAbortException ex)
    {
        running = false;
    }
    finally
    {
        Debug.Print("6. End");
    }
}

public static void Thread_7()
{
    Debug.Print("7. Start");

    heater.Write(false);
    for (int i = 0; i < 14; i++)
    {
        solenoid.Write(false);
        Thread.Sleep(20 * SecondMs);
        solenoid.Write(true);
        Thread.Sleep(1 * DayMs);

        Debug.Print("7. Running. " + i.ToString());
    }
    heater.Write(true); // added this

    Debug.Print("7. End");
}

public static void Thread_8()
{
    Debug.Print("8. Start");

    drain.Write(false);
    Thread.Sleep(16 * MinuteMs);
    solenoid.Write(false);
    Thread.Sleep(16 * MinuteMs);
    Thread.Sleep(30 * MinuteMs);

    Debug.Print("8. End");
}


#12 ErikN

ErikN

    Advanced Member

  • Members
  • PipPipPip
  • 119 posts
  • LocationNew York, NY

Posted 11 April 2012 - 11:19 PM

I'm so sorry for not responding sooner! My watch on this thread seems to have gotten lost! I guess this explains why perkunas emailed me directly!

This is the code I provided (the helper function is the main bit) and perkunas was able to integrate it and get it to work but he's seeing some strange behavior on some pins when the board is first turned on that he's still trying to diagnose. I thought it might be normal board behavior but it sounds not quite right. He says it stabilizes after 5 seconds or so. Hopefully someone more knowledgeable of the board can help here.

Before I paste the code - the other approaches are pretty much the same and probably lighter weight than this but since perkunas is actually using this at the moment I include it so everyone can be on the same page (so the hardware guys don't blame the software too soon or unjustly!) Again, the AwaitCompletionOf is an adaptation of a function that was heavily inspired by forum member Corey Kosak.

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;



namespace Example
{
  public class Program
  {
      public const int SecondMs = 1000;
      public const int MinuteMs = 60 * SecondMs;
      public const int HourMs = 60 * MinuteMs;
      public const int DayMs = 24 * HourMs;
      public const int WeekMs = 7 * DayMs;

      static OutputPort ph = new OutputPort(Pins.GPIO_PIN_D2, true);
      static OutputPort b = new OutputPort(Pins.GPIO_PIN_D3, true);
      static OutputPort a = new OutputPort(Pins.GPIO_PIN_D4, true);
      static OutputPort drain = new OutputPort(Pins.GPIO_PIN_D5, true);
      static OutputPort solenoid = new OutputPort(Pins.GPIO_PIN_D6, true);
      static OutputPort heater = new OutputPort(Pins.GPIO_PIN_D7, true);
      static OutputPort controller = new OutputPort(Pins.GPIO_PIN_D8, true);


      public delegate void VoidAction();

      public static void Main()
      {
          try
          {
              while (true)
              {
                  Method_1();
                  AwaitCompletionOf(new VoidAction[] { Method_2, Method_3, Method_4, Method_5 });
                  AwaitCompletionOf(new VoidAction[] { Method_6, Method_7 });
                  Method_8();
                  Thread.Sleep(10000);
              }
          }
          catch
          {
              //Something has gone wrong; reset to a safe condition
          }
      }

      void RunTheStuff()
      {
      }

      public static void Method_1()
      {
          drain.Write(false);
          Thread.Sleep(16 * MinuteMs);
          drain.Write(true);
      }
      public static void Method_2()
      {
          solenoid.Write(false);
          Thread.Sleep(16 * MinuteMs);
          solenoid.Write(true);
      }
      public static void Method_3()
      {
          a.Write(false);
          Thread.Sleep(11 * MinuteMs + 7 * SecondMs);
          a.Write(true);
      }
      public static void Method_4()
      {
          b.Write(false);
          Thread.Sleep(6 * MinuteMs + 3 * SecondMs);
          b.Write(true);
      }
      public static void Method_5()
      {
          ph.Write(false);
          Thread.Sleep(3 * SecondMs);
          ph.Write(true);
      }
      public static void Method_6()
      {
          while (true)
          {
              controller.Write(false);
              Thread.Sleep(1 * SecondMs);
              controller.Write(true);
              Thread.Sleep(10 * MinuteMs);
          }
      }
      public static void Method_7()
      {
          heater.Write(false);
          for (int i = 0; i < 14; i++)
          {
              solenoid.Write(false);
              Thread.Sleep(20 * SecondMs);
              solenoid.Write(true);
              Thread.Sleep(1 * DayMs);

          }
          heater.Write(false); // added this
      }
      public static void Method_8()
      {
          drain.Write(false);
          Thread.Sleep(16 * MinuteMs);
          solenoid.Write(false);
          Thread.Sleep(16 * MinuteMs);
          Thread.Sleep(30 * MinuteMs);


      }

      public static void AwaitCompletionOf(VoidAction[] actions, int millsecondsTimeout = -1)
      {
          ManualResetEvent mre = new ManualResetEvent(false);
          Thread t = null;
          int total = 0;
          int target = actions.Length;

          for (int i = 0; i < target; i++)
          {
              if (null != t)
                  t.Start();

              int captured = i;

              t = new Thread(() =>
              {
                  try
                  {
                      actions[captured]();
                  }
                  finally
                  {
                      if (Interlocked.Increment(ref total) == target)
                          mre.Set();
                  }
              });
          }

          if (null != t)
          {
              t.Start();
              mre.WaitOne(millsecondsTimeout, false);
          }
      }
  }
}


#13 peterfjorgensen

peterfjorgensen

    New Member

  • Members
  • Pip
  • 9 posts
  • LocationDenmark

Posted 12 April 2012 - 02:44 PM

Hi ErikN and percunas, Erik, I agree that your code looks nice and also will work (if all threads terminate), but it has the same problem as the initial code from percunas: Thread 6 never ends (as far as I can see)! This means that thread 7 and thread 8 will never run and the whole process is stuck in thread 6. Either you have to change the "while(true)" in thread 6 or you have to implement a way to terminate thread 6 from running. Try to add the "Debug.Print" statements to your methods/threads and speed up the execution by setting the SecondMs variable to 1 (maybe also tweak the code in thread 7 so it does not loop as many times...). I hope this helps. best regards Peter F. Jørgensen




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.