Pergunta

I am trying to create a thread which will continuously check for changes to a value, then visually show that change in a PictureBox located in my GUI.

What I actually wrote is a bit more complicated, so I simplified it while keeping the basic idea, I would be happy to provide clarification if this isn't enough:

public class CheckPictures
{
    PictureBox update;
    List<String> check;

    public CheckPictures(PictureBox anUpdate, List<String> aCheck)
    {
        update = anUpdate;
        check = aCheck;
    }

    public void start()
    {
        while(true)
        {
            if (aCheck[0] == "Me")
            {
                update.Image = Image.fromFile("");
            }
        }
    }
}

static int Main(string[] args)
{ 
    List<String> picturesList = new List<String>();

    CheckPictures thread1 = new CheckPictures(PictureBox1, picturesList);
    Thread oThread1 = new Thread(thread1.start));
}

What I want it to do is dynamically change the picture in PictureBox1 if I were to add the string "Me" to pictureList. The above code isn't working like I'd hoped. I had thought that by passing the actual PictureBox and List, any changes to the List elsewhere is the program would be caught by the thread. So my first question is: Is this possible? And if so, what change would I need to make to my code to achieve it?

Foi útil?

Solução

You definetely do not want to do an infinite loop, this will just consume cpu:

while(true)
{
      if (aCheck[0] == "Me")
      {
            update.Image = Image.fromFile("");
      }
 }

I think you should look into the CountdownLatch class.

public class CountdownLatch
  {
    private int m_remain;
    private EventWaitHandle m_event;

    public CountdownLatch(int count)
    {
      m_remain = count;
      m_event = new ManualResetEvent(false);
    }

    public void Signal()
    {
      // The last thread to signal also sets the event.
      if (Interlocked.Decrement(ref m_remain) == 0)
        m_event.Set();
    }

    public void Wait()
    {
      m_event.WaitOne();
    }
  }

The basic idea here is that you need to stop execution on your thread for some time and resume whenever a certain condition has been met (perhaps on another thread).

In other words, you will have a counter, decrement its value on certain condition and whenever it goes to zero you fire your event, execute some code and then start over (stop execution and wait for the counter to go to zero).

In your case you could set the counter to 1 and decrement its value whenever you've set aCheck[0] = "Me"; This way you don't waste CPU.

Pseudo code:

Initialize counter:

CountdownLatch latch = new CountdownLatch(1);

Make thread wait:

public void start()
{
    while(true)
    {
      latch.Wait(); //execution stops
      {
          //execution resumes once the latch counter is zero.
          if (aCheck[0] == "Me")  //double check you have what you need
          {
              update.Image = Image.fromFile("");
              latch = new CountdownLatch(1); //reset if you need to do it again
          }
      }
    }
}

Whenever your condition is met (i.e. aCheck[0] = "Me";) signal your latch:

latch.Signal();

this last line will make the thread resume execution. Good stuff.

Outras dicas

You might want to use events. You register an eventhandler and when something changes in one thread it calls an event handler in the other to do the work. Busy waiting wastes cpu.

Create some object, which will raise event, when new picture was added. E.g. class representing pictures collection:

public class PicturesCollection
{
    public event EventHandler<PictureAddedEventArgs> PictureAdded;
    private List<string> _pictures = new List<string>();

    public void Add(string name)
    {
        _pictures.Add(name);
        if (PictureAdded != null)
            PictureAdded(this, new PictureAddedEventArgs(name));
    }

    public IEnumerable<string> Pictures
    {
        get { return _pictures; }
    }
}

If you want to provide some additional data to event, create custom EventArgs:

public class PictureAddedEventArgs : EventArgs
{
    public PictureAddedEventArgs(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }
}

All you need now - create pictures collection and subscribe to that event:

static int Main(string[] args)
{ 
    PicturesCollection pictures = new PicturesCollection();
    pictures.PictureAdded += Pictures_PictureAdded;
}

static void Pictures_PictureAdded(object sender, PictureAddedEventArgs e)
{
    if (e.Name == "Me")
        PictureBox1.Image = Image.fromFile("");
}

If you add somewhere in your application new picture to collection, it will raise PictureAdded event, which you can handle and update PictureBox. CPU is not wasted in this case.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top