문제

이 간단한 방법이 있다고 말합니다.

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 까다로울 수 있습니다 ... 그래도 생각해 볼게요. 원자 적으로 수행 한 스레드 안전 장치에 "ThreadSafe"반복자를 랩핑해야한다고 생각합니다. MoveNext() 그리고 Current 위에 표시된 인터페이스를 구현합니다. 나는 당신이 어떻게이 인터페이스를 다시 감싸는 지 모르겠습니다. IEnumerator<T> 당신이 그것을 사용할 수 있도록 foreach 그렇지만...

.NET 4.0을 사용하는 경우 평행 연장을 사용합니다 5월 당신을 도울 수 있습니다 - 당신은 당신이하려는 일에 대해 더 설명해야합니다.

이것은 흥미로운 주제입니다 - 나는 그것에 대해 블로그를해야 할 수도 있습니다 ...

편집 : 지금 그것에 대해 블로그를 작성했습니다 두 가지 접근 방식으로.

다른 팁

방금이 코드를 테스트했습니다.

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 ()는 항상 코드의 마지막 부분을 호출합니다. ienumerable이 스레드 안전을 원한다면 WriteLines 대신 원하는 스레드 잠금 장치를 추가하고 ReaderWritersLimlock, Semaphore, Monitor 등을 사용하여 Wither를 추가하십시오.

나는 당신이 나사산 열거자를 필요로한다고 가정하므로 아마도 그를 구현해야 할 것입니다.

글쎄, 나는 확실하지 않지만 발신자의 잠금 장치가 있을까요?

초안:

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