简体   繁体   中英

Pattern for knowing when a method can be called in a new thread

I have a Device interface:

public interface IDevice
{
    double Measure();
}

Some classes that implement that interface have a measuring instrument:

public class InstrumentedDevice : IDevice
{
    public MeasuringInstrument Instrument { get; set; }

    public double Measure()
    {
        if (Instrument != null)
            return Instrument.DoMeasuring();
        return 0;
    }
}

I'd like to group the instances of InstrumentedDevice according to their Instrument property so that the result is a collection of groups in which each device uses the same instrument as all the other devices in its group.

I'd then like to start a new thread for each group and perform the measuring in parallel.

That would look something like this:

public void MeasureAllDevices(IEnumerable<InstrumentedDevice> devices)
{
    var groups = devices.GroupBy(d => d.Instrument);
    foreach (var gr in groups)
    {
        var bgw = new BackgroundWorker();
        bgw.DoWork += (s, e) =>
        {
            foreach (var device in gr)
            {
                device.Measure();
            }
        };
        bgw.RunWorkerAsync();        
    }
}

The problem is that I don't get a collection of InstrumentedDevice as the parameter for MeasureAllDevices . I get a collection of IDevice :

public void MeasureAllDevices(IEnumerable<IDevice> devices)
{

}

My question is this : Is there a pattern I can follow to solve my problem? Not all devices will have a MeasuringInstrument , and some devices may have different means of determining whether or not they can be measured in parallel.

I'd like to add something to the IDevice interface like CanBeMultiThreadedWith(IDevice other) , but I'm not sure how that would work.

public interface IDevice
{
    string GroupBy {get;}
    double Measure();
}

Ok, my first answer misunderstood the problem. Here is a new one:

public interface IDevice
{
    double Measure();
    string ConcurrencyGroupName { get; }
}

Every device gets a new "concurrency group name". The convention is that only device who have this name equal can be processed in parallel.

So you .GroupBy the ConcurrencyGroupName and foreach group you process its items in parallel.

This way the devices decide if they want to execute in parallel or not. Your central piece of code that does the processing will never have to be modified.

Your InstrumentedDevice class would just return the name of the instrument or its ID as the ConcurrencyGroupName. Other implementations would be possible, too.

You need to split the devices in ones that are instrumented and ones that aren't:

var measurable = devices.OfType<InstrumentedDevice>().ToList();
var notMeasurable = devices.Except(measurable).ToList();

You can process the two sets independently.

public static MeasuringInstrument Instrument(this IDevice device)
{
  if (device is InstrumentedDevice)
  {
    return (device as InstrumentedDevice).Instrument;
  }

  return null;
}


var groups = devices.GroupBy(d => d.Instrument());
foreach (var gr in groups) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM