Разработка альтернатив потоковой реализации сопрограмм для преобразования метода push в метод pull - PullRequest
1 голос
/ 27 сентября 2011

У меня есть класс коллекции, который содержит много разных типов данных в сжатом формате. Для перечисления всех значений в коллекции есть метод Execute(Query, IDataWriter). Вы передаете ему запрос, который определяет, какие данные вам нужны, а затем для каждого фрагмента совпадающих данных он вызывает метод объекта IDataWriter, который вы передаете.

Интерфейс IDataWriter имеет 15 различных методов, по одному для каждого типа данных в коллекции. Теперь мне нужно зафиксировать эти данные в базе данных, и я хочу иметь возможность реализовать IEnumerator<SqlDataRecord> для фиксации материала в базе данных. Проблема заключается в том, как преобразовать вызов Execute, который сбрасывает тонну данных в объект IDataWriter (push), в метод pull, чтобы можно было использовать MoveNext и Current в IEnumerator.

Я посмотрел на сопрограммы и волокна, но ни один из примеров, которые я обнаружил, не показался бы работающим для существующего метода (в моем случае - Execute), который внутренне ничего не знает о корунтине. Так что мой план в псевдокоде состоит в том, чтобы сделать следующее, используя потоки и ручную синхронизацию.

Метод Execute будет выполняться в отдельном потоке, и я буду вручную ждать и сигнализировать его внутри каждого метода IDataWriter

class EnumeratorAdapterObject : IEnumerator<SqlDataRecord>, IDataWriter
{

public EnumeratorAdapterObject(Store storeObject)
{
    workerThread = new Thread(storeObject.Execute(query, this));
}

public bool MoveNext()
{
    if (firstTimeCalled)
    {
        start worker thread
    }
    else
    {
        signal resume
    }
    block for either a call into an Add method or the Execute thread finishes

    if (not anything buffered)
        return false
    else
        return true
}


// 14 other methods like this implemented in IDataWriter, each with different types
public void Add_Decimal(IntvlDataHeader header, decimal data)
{
    buffer field of current SqlDataRecord = generate record;

    signal main thread
    wait for resume signal
}

public SqlDataRecord Current
{
    get { return buffer field of current SqlDataRecord; }
}

}

Это похоже на хороший подход? Кто-нибудь знает какие-либо примеры или вопросы, которые уже реализуют это?

Или можно было бы воспользоваться любой из новых функций 4.0? Я думал об использовании блокирующей параллельной коллекции с ограничением в 1 вещь, но тогда как бы потребитель (MoveNext IEnumerator) узнал, когда другой поток завершит добавление материала?

1 Ответ

0 голосов
/ 28 сентября 2011

Вместо того чтобы делать ручное создание потоков и синхронизацию с сигналом / ожиданием, я понял, что могу использовать коллекцию блокировок с вызовом CompleteAdding(), когда это будет сделано.Ниже приведен быстрый пример, для моей задачи выше я оберну это в объект, который реализует IEnumerator<SqlDataRecord> и IDataWriter, поэтому вместо вызова GenerateStuff я буду вызывать Execute, а методы Add_ * будут вызывать col.Add (new SqlDataRecord (....))

static void Main(string[] args)
{
    var col = new BlockingCollection<int>(1);

    Task.Factory.StartNew(
        () =>
        {
            GenerateStuff(col);
            col.CompleteAdding();
        });

    while (!col.IsCompleted)
    {
        Thread.Sleep(100);

        int result;
        if (!col.TryTake(out result, -1))
        {
            break;
        }
        Console.WriteLine("Got {0}", result);
    }

    Console.WriteLine("Done Adding!");
}

static void GenerateStuff(BlockingCollection<int> col)
{
    for (int i = 0; i < 10; i++)
    {
        Thread.Sleep(10);
        Console.WriteLine("Adding {0}", i);
        col.Add(i);
        Console.WriteLine("Added {0}", i);
    }
}

Это также имеет то преимущество, что рабочий поток, который выполняет Execute, будет генерировать следующий результат одновременно с IEnumerator, возвращающим Current и sqlкод делает все, что он делает для фиксации данных.С ручным сигналом потока будет работать только один поток за один раз.

...