async Task MyMethodAsync()
{
    int val = 10;
    await Task.Delay(TimeSpan.FromSeconds(1));
    val = val + 1;
    await Task.Delay(TimeSpan.FromSeconds(1));
    val = val - 1;
    await Task.Delay(TimeSpan.FromSeconds(1));
    Trace.WriteLine(val);
}

---

class SharedData
{
    public int Value { get; set; }
}

async Task ModifyValueAsync(SharedData data)
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    data.Value = data.Value + 1;
}

// UWAGA: może wymagać synchronizacji; zobacz dyskusję poniżej.
async Task<int> ModifyValueConcurrentlyAsync()
{
    var data = new SharedData();

    // Rozpoczęcie trzech współbieżnych modyfikacji.
    var task1 = ModifyValueAsync(data);
    var task2 = ModifyValueAsync(data);
    var task3 = ModifyValueAsync(data);

    await Task.WhenAll(task1, task2, task3);
    return data.Value;
}

---

private int value;

async Task ModifyValueAsync()
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    value = value + 1;
}

// UWAGA: może wymagać synchronizacji; zobacz dyskusję poniżej.
async Task<int> ModifyValueConcurrentlyAsync()
{
    // Rozpoczyna trzy współbieżne modyfikacje.
    var task1 = ModifyValueAsync();
    var task2 = ModifyValueAsync();
    var task3 = ModifyValueAsync();

    await Task.WhenAll(task1, task2, task3);
    return value;
}

---

// ZŁY KOD!!!
async Task<int> SimpleParallelismAsync()
{
    int val = 0;
    var task1 = Task.Run(() => { val = val + 1; });
    var task2 = Task.Run(() => { val = val + 1; });
    var task3 = Task.Run(() => { val = val + 1; });
    await Task.WhenAll(task1, task2, task3);
    return val;
}

---

void IndependentParallelism(IEnumerable<int> values)
{
    Parallel.ForEach(values, item => Trace.WriteLine(item));
}

---

// ZŁY KOD!!!
int ParallelSum(IEnumerable<int> values)
{
    int result = 0;
    Parallel.ForEach(source: values,
        localInit: () => 0,
        body: (item, state, localValue) => localValue + item,
        localFinally: localValue => { result += localValue; });
    return result;
}

---

async Task<bool> PlayWithStackAsync()
{
    var stack = ImmutableStack<int>.Empty;

    var task1 = Task.Run(() => Trace.WriteLine(stack.Push(3).Peek()));
    var task2 = Task.Run(() => Trace.WriteLine(stack.Push(5).Peek()));
    var task3 = Task.Run(() => Trace.WriteLine(stack.Push(7).Peek()));
    await Task.WhenAll(task1, task2, task3);
    
    return stack.IsEmpty; // Zawsze zwraca true.
}

---

// ZŁY KOD!!!
async Task<bool> PlayWithStackAsync()
{
    var stack = ImmutableStack<int>.Empty;

    var task1 = Task.Run(() => { stack = stack.Push(3); });
    var task2 = Task.Run(() => { stack = stack.Push(5); });
    var task3 = Task.Run(() => { stack = stack.Push(7); });
    await Task.WhenAll(task1, task2, task3);

    return stack.IsEmpty;
}

---

async Task<int> ThreadsafeCollectionsAsync()
{
    var dictionary = new ConcurrentDictionary<int, int>();

    var task1 = Task.Run(() => { dictionary.TryAdd(2, 3); });
    var task2 = Task.Run(() => { dictionary.TryAdd(3, 5); });
    var task3 = Task.Run(() => { dictionary.TryAdd(5, 7); });
    await Task.WhenAll(task1, task2, task3);

    return dictionary.Count; // Zawsze zwraca 3.
}

---

class MyClass
{
    // Ta blokada chroni pole _value.
    private readonly object _mutex = new object();

    private int _value;

    public void Increment()
    {
        lock (_mutex)
        {
            _value = _value + 1;
        }
    }
}

---

class MyClass
{
    // Ta blokada chroni pole _value.
    private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);

    private int _value;

    public async Task DelayAndIncrementAsync()
    {
        await _mutex.WaitAsync();
        try
        {
            var oldValue = _value;
            await Task.Delay(TimeSpan.FromSeconds(oldValue));
            _value = oldValue + 1;
       }
       finally
       {
            _mutex.Release();
        }
    }
}

---

class MyClass
{
    // Ta blokada chroni pole _value.
    private readonly AsyncLock _mutex = new AsyncLock();

    private int _value;

    public async Task DelayAndIncrementAsync()
    {
        using (await _mutex.LockAsync())
        {
            var oldValue = _value;
            await Task.Delay(TimeSpan.FromSeconds(oldValue));
            _value = oldValue + 1;
        }
    } 
}

---

class MyClass
{
    private readonly ManualResetEventSlim _initialized =
        new ManualResetEventSlim();

    private int _value;

    public int WaitForInitialization()
    {
        _initialized.Wait();
        return _value;
    }

    public void InitializeFromAnotherThread()
    {
        _value = 13;
        _initialized.Set();
    }
}

---

class MyClass
{
    private readonly TaskCompletionSource<object> _initialized =
        new TaskCompletionSource<object>();

    private int _value1;
    private int _value2;

    public async Task<int> WaitForInitializationAsync()
    {
        await _initialized.Task;
        return _value1 + _value2;
    }

    public void Initialize()
    {
        _value1 = 13;
        _value2 = 17;
        _initialized.TrySetResult(null);
    }
}

---

class MyClass
{
    private readonly AsyncManualResetEvent _connected =
        new AsyncManualResetEvent();

    public async Task WaitForConnectedAsync()
    {
        await _connected.WaitAsync();
    }

    public void ConnectedChanged(bool connected)
    {
        if (connected)
            _connected.Set();
        else
            _connected.Reset();
    }
}

---

IPropagatorBlock<int, int> DataflowMultiplyBy2()
{
    var options = new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = 10
    };

    return new TransformBlock<int, int>(data => data * 2, options);
}

// Użycie Parallel LINQ (PLINQ)
IEnumerable<int> ParallelMultiplyBy2(IEnumerable<int> values)
{
    return values.AsParallel()
        .WithDegreeOfParallelism(10)
        .Select(item => item * 2);
}

// Użycie klasy Parallel
void ParallelRotateMatrices(IEnumerable<Matrix> matrices, float degrees)
{
    var options = new ParallelOptions
    {
        MaxDegreeOfParallelism = 10
    };
    Parallel.ForEach(matrices, options, matrix => matrix.Rotate(degrees));
}

---

async Task<string[]> DownloadUrlsAsync(IEnumerable<string> urls)
{
    var httpClient = new HttpClient();
    var semaphore = new SemaphoreSlim(10);
    var tasks = urls.Select(async url =>
    {
        await semaphore.WaitAsync();
        try
        {
            return await httpClient.GetStringAsync(url);
        }
        finally
        {
            semaphore.Release();
        }
    }).ToArray();
    return await Task.WhenAll(tasks);
}
