سؤال

قل لدي هذه الطريقة البسيطة:

public IEnumerable<uint> GetNumbers()
{
    uint n = 0;
    while(n < 100)
        yield return n++;
}

كيف ستجعل هذا الموضوع آمنًا؟ وأعني بذلك أنك ستحصل على هذا العداد مرة واحدة ، ولديك عدة مؤشرات ترابط مع جميع الأرقام دون الحصول على تكرارات.

أفترض أن هناك حاجة إلى استخدام قفل في مكان ما ، ولكن أين يجب أن يكون هذا القفل ليكون كتلة التكرار آمنة؟ ما ، بشكل عام ، هل تحتاج إلى تذكر ما إذا كنت تريد مؤشر ترابط آمن IEnumerable<T>؟ أو بالأحرى أعتقد أنه سيكون موضوعًا آمنًا IEnumerator<T>...?

هل كانت مفيدة؟

المحلول

هناك مشكلة متأصلة في القيام بذلك ، لأن IEnumerator<T> لديه كلاهما MoveNext() و Current. تريد حقًا مكالمة واحدة مثل:

bool TryMoveNext(out T value)

في تلك المرحلة يمكنك ذري انتقل إلى العنصر التالي واحصل على قيمة. تنفيذ ذلك وما زال قادرًا على الاستخدام yield يمكن أن يكون صعبًا ... سأفكر في الأمر. أعتقد أنك ستحتاج إلى لفت "غير الرصيف" في واحد آمن من مؤشرات الترابط التي أجرتها ذرية MoveNext() و Current لتنفيذ الواجهة الموضحة أعلاه. لا أعرف كيف يمكنك بعد ذلك لف هذه الواجهة مرة أخرى IEnumerator<T> حتى تتمكن من استخدامه في foreach على أية حال...

إذا كنت تستخدم .NET 4.0 ، امتدادات متوازية مايو تكون قادرًا على مساعدتك - ستحتاج إلى شرح المزيد حول ما تحاول القيام به.

هذا موضوع مثير للاهتمام - قد أضطر إلى التدوين حوله ...

تحرير: أنا الآن تم التدوين حول هذا الموضوع مع اثنين من النهجين.

نصائح أخرى

لقد اختبرت للتو هذا الجزء من التعليمات البرمجية:

static IEnumerable<int> getNums()
{
    Console.WriteLine("IENUM - ENTER");

    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(i);
        yield return i;
    }

    Console.WriteLine("IENUM - EXIT");
}

static IEnumerable<int> getNums2()
{
    try
    {
        Console.WriteLine("IENUM - ENTER");

        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine(i);
            yield return i;
        }
    }
    finally
    {
        Console.WriteLine("IENUM - EXIT");
    }
}

GetNums2 () يدعو دائمًا الجزء أخيرًا من الكود. إذا كنت تريد أن تكون مؤشر ترابطك آمنًا ، فأضف أي قوائم خيط تريدها بدلاً من كتاب WriteLines ، وذوقك باستخدام ReaderWritersLiMlock ، و Smaphore ، و Monitor ، إلخ ، إلخ.

أفترض أنك تحتاج إلى تعداد لخيوط الخيط ، لذلك ربما يجب عليك تنفيذ ذلك.

حسنًا ، لست متأكدًا ، لكن ربما مع بعض الأقفال في المتصل؟

مسودة:

Monitor.Enter(syncRoot);
foreach (var item in enumerable)
{
  Monitor.Exit(syncRoot);
  //Do something with item
  Monitor.Enter(syncRoot);
}
Monitor.Exit(syncRoot);

كنت أفكر في أنه لا يمكنك عمل yield آمن مؤشر ترابط الكلمات الرئيسية ، ما لم تجعله يعتمد على مصدر قيم آمن بالفعل مؤشر ترابط:

public interface IThreadSafeEnumerator<T>
{
    void Reset();
    bool TryMoveNext(out T value);
}

public class ThreadSafeUIntEnumerator : IThreadSafeEnumerator<uint>, IEnumerable<uint>
{
    readonly object sync = new object();

    uint n;

    #region IThreadSafeEnumerator<uint> Members
    public void Reset()
    {
        lock (sync)
        {
            n = 0;
        }
    }

    public bool TryMoveNext(out uint value)
    {
        bool success = false;

        lock (sync)
        {
            if (n < 100)
            {
                value = n++;
                success = true;
            }
            else
            {
                value = uint.MaxValue;
            }
        }

        return success;
    }
    #endregion
    #region IEnumerable<uint> Members
    public IEnumerator<uint> GetEnumerator()
    {
        //Reset(); // depends on what behaviour you want
        uint value;
        while (TryMoveNext(out value))
        {
            yield return value;
        }
    }
    #endregion
    #region IEnumerable Members
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        //Reset(); // depends on what behaviour you want
        uint value;
        while (TryMoveNext(out value))
        {
            yield return value;
        }
    }
    #endregion
}

سيتعين عليك أن تقرر ما إذا كان يجب على كل بدء نموذجي للعداد إعادة تعيين التسلسل ، أو ما إذا كان رمز العميل يجب أن يفعل ذلك.

يمكنك فقط إرجاع تسلسل كامل في كل مرة بدلاً من استخدام العائد:

return Enumerable.Range(0, 100).Cast<uint>().ToArray();

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top