---
# System prepended metadata

title: 'Ćwiczenia 3, grupa śr. 12-14, 9 listopada 2022'
tags: [PRW22, pwit, ćwiczenia]

---

# Ćwiczenia 3, grupa śr. 12-14, 9 listopada 2022

###### tags: `PRW22` `ćwiczenia` `pwit`

## Deklaracje

Gotowość rozwiązania zadania należy wyrazić poprzez postawienie X w odpowiedniej kolumnie! Jeśli pożądasz zreferować dane zadanie (co najwyżej jedno!) w trakcie dyskusji oznacz je znakiem ==X== na żółtym tle.

**UWAGA: Tabelkę wolno edytować tylko wtedy, gdy jest na zielonym tle!**



:::danger
|                        | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   |
| ----------------------:| ----- | --- | --- | --- | --- | --- | --- | --- |
Michał Bronowicki        |       |     |     |     |     |     |     |     |  
Wiktor Bukowski          |   X   |  X  |  X  |  X  |  X  |  X  |     |     |
Jan Dalecki              |   X    |     |  X   |  X   |  X   |  X   | X    |     |
Mikołaj Depta            |   X   |==X==|  X  |  X  |     |     |  X  |     |
Kamil Galik              | ==X== |  X  |  X  |  X  |     |     | X   |     |
Bartosz Głowacki         |   X   |  X  |  X  |  X  |     |     |     |     |
Michał Hetnar            |   x   | x   |  x  | x   |     |     | x    |     |
Adam Jarząbek            |       |     |     |     |     |     |     |     |
Michał Kierul            |       |     |     |     |     |     |     |     |
Mateusz Kisiel           |   x   |  x  |  x  |==x==|  x  |  x  |     |     |
Maksymilian Komarnicki   |   X   |  X  |  X  |  X  |  X  |     |     |     |
Julia Matuszewska        |   X   |  X  |  X  |  X  |     |     |  X  |     |
Andrzej Morawski         |       |     |     |     |     |     |     |     |
Wojciech Pokój           |   X   |  X  |  X  |  X  |  X  |  X  |     |     |
Marcin Sarnecki          |   X   |  X  |  X  |==X==|     |     |     |     |
Bartosz Szczeciński      |   X    |   X  |   X  |     |     |     |     |     |
Tomasz Wołczański        |   X   |  X  |  X  |  X  |     |     |     |  X  |
Marcin Wróbel            |   X   |  X  |  X  |  X  |  X  |  X  |  X  |     |
:::




:::info
**Uwaga:** Po rozwiązaniu zadania należy zmienić kolor nagłówka na zielony.
:::



## Zadanie 1
:::success
Autor: Kamil Galik	
:::

```java
package zad1;

class MergeSort implements Runnable {
    protected int arr[];
    protected static int helper_arr[];
    protected int l, r;

    MergeSort(int arr[], int l, int r) {
        this.arr = arr;
        this.l = l;
        this.r = r;
    }

    private void prepare_helper_array(int arr[], int l, int m, int r, int left_size, int right_size) {
        for (int i = 0; i < left_size; i++) {
            helper_arr[l + i] = arr[l + i];
        }
        for (int i = 0; i < right_size; i++) {
            helper_arr[m + 1 + i] = arr[m + 1 + i];
        }
    }

    private void merge_to_arr(int[] arr, int l, int m, int r, int left_size, int right_size) {
        int i = 0, j = 0;
        int k = l;
        while (i < left_size && j < right_size) {
            if (helper_arr[l + i] <= helper_arr[m + 1 + j]) {
                arr[k] = helper_arr[l + i];
                i += 1;
            } else {
                arr[k] = helper_arr[m + 1 + j];
                j += 1;
            }
            k += 1;
        }
        while (i < left_size) {
            arr[k] = helper_arr[l + i];
            k += 1;
            i += 1;
        }
        while (j < right_size) {
            arr[k] = helper_arr[m + 1 + j];
            k += 1;
            j += 1;
        }
    }

    private void merge(int l, int m, int r) {
        int left_size = m - l + 1;
        int right_size = r - m;

        prepare_helper_array(arr, l, m, r, left_size, right_size);
        merge_to_arr(arr, l, m, r, left_size, right_size);
    }

    public void sort(int l, int r) {
        if (l < r) {
            int m = (l + r) / 2;
            sort(l, m);
            sort(m + 1, r);
            merge(l, m, r);
        }
    }

    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.printf("Started thread ID: %d\n", t.getId());
        if (l < r) {
            int m = (l + r) / 2;
            MergeSort left = new MergeSort(arr, l, m);
            MergeSort right = new MergeSort(arr, m + 1, r);
            Thread left_thread = new Thread(left);
            Thread right_thread = new Thread(right);
            left_thread.start();
            right_thread.start();
            try {
                left_thread.join();
                right_thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.merge(l, m, r);
        }
    }
}

public class RookieMergeSort {

    public static void main(String[] args) {
        int arr[] = { 4, 3, 2, 1, 6, 7, 8, 5 };
        MergeSort.helper_arr = new int[8];
        MergeSort ms = new MergeSort(arr, 0, 7);

        Thread ms_thread = new Thread(ms);

        ms_thread.start();
        try {
            ms_thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 8; i++)
            System.out.printf("%d ", arr[i]);

    }
}
```

## Zadanie 2
:::success
Autor: Mikołaj Depta	
:::

```java
import java.io.Console;

class MergeSort implements Runnable {

    final int THRESHOLD = 32;
    protected int temporary[];
    protected int arr[];
    protected int l, r;
    MergeSort(int arr[], int temp[], int l, int r) {
        System.out.println("New Sorter thread");
        this.arr = arr;
        this.temporary = temp;
        this.l = l;
        this.r = r;
    }

    private synchronized void merge(int l, int m, int r) {
        int left_index = l;
        int right_index = m + 1;
        int left_range = m + 1;
        int right_range = r + 1;

        for(int i = left_index; i < right_range; i++) {
            temporary[i] = arr[i];
        }

        int main_index = left_index;
        while (left_index < left_range && right_index < right_range) {
            if (temporary[left_index] <= temporary[right_index]) {
                arr[main_index] = temporary[left_index];
                left_index += 1;
            } else {
                arr[main_index] = temporary[right_index];
                right_index += 1;
            }
            main_index += 1;
        }
        while (left_index < left_range) {
            arr[main_index] = temporary[left_index];
            main_index += 1;
            left_index += 1;
        }
        while (right_index < right_range) {
            arr[main_index] = temporary[right_index];
            main_index += 1;
            right_index += 1;
        }
    }

    public void sort_one_thread(int l, int r) {
        if (l < r) {
            int m = (l + r) / 2;
            sort_one_thread(l, m);
            sort_one_thread(m + 1, r);
            merge(l, m, r);
        }
    }


    @Override
    public void run() {
        if (this.l < this.r) {
            if(this.r - this.l + 1 < THRESHOLD) {
                System.out.println("Sorting in one thread. Size: " + (this.r - this.l + 1));
                this.sort_one_thread(this.l, this.r);
            } else {
                int m = (this.l + this.r) / 2;
                MergeSort left_sub_array_sorter = new MergeSort(arr, temporary, this.l, m);
                Thread left_thread = new Thread(left_sub_array_sorter);
                left_thread.start();
                int prev_l = this.l;
                this.l = m + 1;
                this.run();
                this.l = prev_l;
                try {
                    left_thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.merge(this.l, m, this.r);
            }
        }
    }
}



```

## Zadanie 3
:::success
Autor: Bartosz Szczeciński	
:::

```java=
class MergeSort3 implements Runnable {
    public static final int MAX_THREADS = 3;
    protected int[] arr;
    protected int[] helperArr;
    protected int left, right;

    private static int threadCount = 0;
    private static final Object threadCountLock = new Object();

    MergeSort3(int[] arr, int left, int right, int[] helperArray) {
        this.arr = arr;
        this.left = left;
        this.right = right;
        this.helperArr = helperArray;
    }

    private void fillHelperArray(int left, int mid, int right){
        if (mid + 1 - left >= 0)
            System.arraycopy(arr, left, helperArr, left, mid + 1 - left);

        if (right - mid >= 0)
            System.arraycopy(arr, mid + 1, helperArr, mid + 1, right - mid);
    }

    private void merge(int left, int mid, int right) {
        fillHelperArray(left, mid, right);

        int leftIdx = left, rightIdx = mid + 1;
        int outIdx = left;
        while (leftIdx <= mid && rightIdx <= right) {
            if (helperArr[leftIdx] <= helperArr[rightIdx]) {
                arr[outIdx] = helperArr[leftIdx];
                leftIdx += 1;
            } else {
                arr[outIdx] = helperArr[rightIdx];
                rightIdx += 1;
            }
            outIdx += 1;
        }
        while (leftIdx <= mid) {
            arr[outIdx] = helperArr[leftIdx];
            outIdx += 1;
            leftIdx += 1;
        }
        while (rightIdx <= right) {
            arr[outIdx] = helperArr[rightIdx];
            outIdx += 1;
            rightIdx += 1;
        }
    }

    public void sort(int l, int r) {
        if (l < r) {
            int m = (l + r) / 2;
            sort(l, m);
            sort(m + 1, r);
            merge(l, m, r);
        }
    }

    private void runChildThreads(int m){
        MergeSort3 left = new MergeSort3(arr, this.left, m, this.helperArr);
        MergeSort3 right = new MergeSort3(arr, m + 1, this.right, this.helperArr);
        Thread t1 = new Thread(left);
        Thread t2 = new Thread(right);
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        boolean isThreadCountExceeded;
        synchronized (threadCountLock) {
            threadCount++;
            isThreadCountExceeded = threadCount > MAX_THREADS - 2;
        }


        if (this.left < this.right) {
            if (isThreadCountExceeded){
                sort(this.left, this.right);
            } else {
                int m = (this.left + this.right) / 2;
                this.runChildThreads(m);
                this.merge(this.left, m, this.right);
            }
        }

        synchronized (threadCountLock) {
            threadCount--;
        }
    }
}
```

## Zadanie 4
:::success
Autor: Mateusz Kisiel	
:::
![](https://i.imgur.com/spsYZw6.png)

```java

public class Zad4 implements Runnable{
    protected int t[];
    protected int l, r;
    protected final int MIN_CONCURRENT_SIZE = 2;

    public Zad4(int[] t, int l, int r) {
        this.t = t;
        this.l = l;
        this.r = r;
    }

    protected int begVal, begAmount = 0;
    protected int endVal, endAmount = 0;
    protected int maxVal, maxAmount = 0;

    private int size(){
        return r-l+1;
    }

    private void process(){
        int currentVal = maxVal = t[l];
        int currentAmount = maxAmount = 1;
        boolean isBegin = true;


        for(int i = l+1; i<=r; i++){
            if(t[i] == currentVal){
                currentAmount++;
                if(currentAmount > maxAmount){
                    maxAmount = currentAmount;
                    maxVal = currentVal;
                }
            }
            else{
                if(isBegin){
                    begAmount = currentAmount;
                    begVal = currentVal;
                    isBegin = false;
                }
                currentAmount = 1;
                currentVal = t[i];
            }
        }
        endAmount = currentAmount;
        endVal = currentVal;
        if(isBegin){
            begAmount = currentAmount;
            begVal = currentVal;
            isBegin = false;
        }
    }

    public void print(){
        System.out.print("[ ");
        for(int i = 0; i<maxAmount; i++){
            System.out.print(maxVal);
            if(i+1 < maxAmount){
                System.out.print(", ");
            }
        }
        System.out.print(" ]");
    }

    @Override
    public void run() {
        if(r-l+1 < MIN_CONCURRENT_SIZE){
            process();
            return;
        }
        int m = (this.l + this.r) / 2;
        Zad4 left = new Zad4(t, l, m);
        Zad4 right = new Zad4(t, m+1, r);
        Thread t1 = new Thread(left);
        Thread t2 = new Thread(right);
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(left.maxAmount > right.maxAmount){
            maxAmount = left.maxAmount;
            maxVal = left.maxVal;
        }
        else{
            maxAmount = right.maxAmount;
            maxVal = right.maxVal;
        }

        this.begAmount = left.begAmount;
        this.begVal = left.begVal;
        this.endAmount = right.endAmount;
        this.endVal = right.endVal;

        if(left.endVal == right.begVal){
            int midAmount = left.endAmount + right.begAmount;
            if(midAmount > maxAmount){
                maxAmount = midAmount;
                maxVal = right.begVal;
            }
            if(left.maxAmount == left.size())
                this.begAmount = midAmount;
            if(right.maxAmount == right.size()){
                this.endAmount = midAmount;
            }
        }

    }
}

```


## Zadanie 5
:::success
Autor: Maksymilian Komarnicki	
:::

```java=
import java.util.concurrent.ConcurrentLinkedQueue;

class ArrayScope {
	protected int first, last;
	protected boolean sorted;	

	ArrayScope(int first, int last) {
		this.first = first;
		this.last = last;
		this.sorted = false;
	}	

	public synchronized boolean isScopeSorted() { return sorted; }

	public synchronized boolean setScopeSorted() { return this.sorted = true; }

	public int getScopeBeg() { return first; }

	public int getScopeEnd() { return last; }
}

class Task {
	protected ArrayScope arrayScope_1, arrayScope_2, arrayScope_3;

	Task(ArrayScope array) {
		this.arrayScope_1 = array;
	}

	Task(ArrayScope leftArr, ArrayScope rightArr, ArrayScope  entireArr) {
		this.arrayScope_1 = leftArr;
		this.arrayScope_2 = rightArr;
		this.arrayScope_3 = entireArr;
	}

	public boolean isSortTask() { return arrayScope_3 == null; }

	public boolean areArraysReadyToMerge() {		
		return !isSortTask() && arrayScope_1.isScopeSorted() && arrayScope_2.isScopeSorted();
	}

	public ArrayScope getScopeToSort() { return arrayScope_1; }

	public ArrayScope getLeftScopeToMerge() { return arrayScope_1; }

	public ArrayScope getRightScopeToMerge() { return arrayScope_2; }

	public ArrayScope getEntireMergedScope() { return arrayScope_3; }
}

class QueueWorker implements Runnable {
	private int[] array, auxArr;	
	private ArrayScope entireArrayScope;	
	private ConcurrentLinkedQueue<Task> queue;

	QueueWorker(int[] array, int[] auxArr, ArrayScope entireArrScope, ConcurrentLinkedQueue<Task> queue) {
		this.array = array;
		this.auxArr = auxArr;
		this.entireArrayScope = entireArrScope;
		this.queue = queue;
	}

	private void sort(Task task) {
		ArrayScope scope = task.getScopeToSort();
		int first = scope.getScopeBeg();
		int last = scope.getScopeEnd();
		if(first < last) {
			int middle = (first + last) / 2;
			ArrayScope left = new ArrayScope(first, middle);
			ArrayScope right = new ArrayScope(middle + 1, last);
			queue.offer(new Task(left));
			queue.offer(new Task(right));
			queue.offer(new Task(left, right, scope));		
		}		
		else {
			scope.setScopeSorted();
		}
	}

 	private void copyArrToAux(int first, int last) {
        for (int i = first; i <= last; i++) {
            auxArr[i] = array[i];
        }
    }

    private void fillArrWithTheRestOfAux(int arrPos, int auxPos, int auxLast) {
        while (auxPos <= auxLast) {
            array[arrPos] = auxArr[auxPos];
            arrPos += 1;
            auxPos += 1;
        }    
    }

	private void merge(Task task) {
		ArrayScope wholeScope = task.getEntireMergedScope();
		int first = wholeScope.getScopeBeg();
		int last = wholeScope.getScopeEnd();
		int middle = task.getLeftScopeToMerge().getScopeEnd();
		copyArrToAux(first, last);

        int i = first, j = middle + 1, k = first;        
        while (i <= middle && j <= last) {
            if (auxArr[i] <= auxArr[j]) {
                array[k] = auxArr[i];
                i += 1;
            } else {
                array[k] = auxArr[j];
                j += 1;
            }
            k += 1;
        }

        fillArrWithTheRestOfAux(k, i, middle);
        fillArrWithTheRestOfAux(k, j, last);
        wholeScope.setScopeSorted();     
	}

	private void solveTask() {
		Task task = queue.poll();
		if(task != null) {
			if(task.isSortTask()) {				 
				sort(task);
			} 
			else if(task.areArraysReadyToMerge()) {				
				merge(task);
			}
			else {			
				queue.offer(task);
			}
		}
	}

	@Override
	public void run() {
		while(!entireArrayScope.isScopeSorted()) {			  
			solveTask();
		}
	}
}

```

## Zadanie 6
:::success
Autor: Wojciech Pokój	
:::

```java=
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;

class MargeSortWorker implements Runnable {
    int arr[], aux[];
    LinkedBlockingQueue<Task> queue;
    MergeSortManager manager;

    public MargeSortWorker(int arr[], LinkedBlockingQueue<Task> queue, MergeSortManager manager) {
        this.arr = arr;
        this.queue = queue;
        this.manager = manager;
    }

    public void SplitArray(Task task) {
        if(task.l >= task.r) {
            task.NotifyTaskDone(queue);
            task.NotifyTaskDone(queue);
            return;
        }

        int m = (task.l + task.r) / 2;
        Task lTask = new Task(task, task.l, m);
        Task rTask = new Task(task, m + 1, task.r);
        
        try {
            queue.put(lTask);
            queue.put(rTask);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void MergeArrays(Task task) {
        int l = task.l, r = task.r, m = task.m;

        int n1 = m - l + 1;
        int n2 = r - m;
        int[] left = new int[n1];
        int[] right = new int[n2];

        for (int i = 0; i < n1; i++) {
            left[i] = arr[l + i];
        }
        for (int i = 0; i < n2; i++) {
            right[i] = arr[m + 1 + i];
        }
        int i = 0, j = 0;
        int k = l;
        while (i < n1 && j < n2) {
            if (left[i] <= right[j]) {
                arr[k] = left[i];
                i += 1;
            } else {
                arr[k] = right[j];
                j += 1;
            }
            k += 1;
        }
        while (i < n1) {
            arr[k] = left[i];
            k += 1;
            i += 1;
        }
        while (j < n2) {
            arr[k] = right[j];
            k += 1;
            j += 1;
        }

        task.NotifyTaskDone(queue);
    }

    @Override
    public void run() {
        while(!manager.isDone()) {
            Task nTask = null;

            try {
                nTask = queue.take();
            }
            catch(InterruptedException e) {
                e.printStackTrace();
                continue;
            }

            if(nTask == null) continue;

            System.out.println("picked task: " + nTask.taskName + " l = " + nTask.l + " r = " + nTask.r);

            switch(nTask.taskName) {
                case MergeSortManager.TaskMerge:
                    MergeArrays(nTask);
                    continue;
                case MergeSortManager.TaskSplit:
                    SplitArray(nTask);
                    continue;
                case MergeSortManager.TaskDone:
                    nTask.NotifyTaskDone(queue);
                    return;
            }
        }
    }
}

class Task {
    public String taskName;
    public int l, r, m;

    private int counter = 0;

    public Task parent;
    

    public Task(Task parent, int l,  int r) { 
        this.parent = parent;
        this.l = l;
        this.r = r;
        this.m = (l + r) / 2;
        this.taskName = MergeSortManager.TaskSplit;
    }

    public synchronized void NotifyTaskDone(LinkedBlockingQueue<Task> queue) {
        if(taskName == MergeSortManager.TaskDone) {
            try {
                queue.put(this);
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }
            return;
        }

        counter++;
        if(counter == 2) {
            taskName = MergeSortManager.TaskMerge;

            try {
                queue.put(this);
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
        if(counter == 3) {
            taskName = MergeSortManager.TaskDone;

            if(parent != null) {
                parent.NotifyTaskDone(queue);
            }
            else {
                try {
                    queue.put(this);
                }
                catch(InterruptedException e) {
                    e.printStackTrace();
                }
                return;
            }
        }
    }

    public boolean isDone() {
        return taskName == MergeSortManager.TaskDone;
    }
}

class MergeSortManager {
    public static final String TaskMerge = "task.merge";
    public static final String TaskSplit = "task.split";
    public static final String TaskDone  = "task.done"; 

    LinkedBlockingQueue<Task> queue;
    Task mainTask;

    public MergeSortManager(int arr[], int count) {
        queue = new LinkedBlockingQueue<Task>();

        mainTask = new Task(null, 0, arr.length - 1);

        try {
            queue.put(mainTask);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }

        Thread workers[] = new Thread[count];

        for(int i = 0; i < count; i++) {
            workers[i] = new Thread(new MargeSortWorker(arr, queue, this));
        }

        for(int i = 0; i < count; i++) {
            workers[i].start();
        }

        try {
            for(int i = 0; i < count; i++) {
                workers[i].join();
            }
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }

    public boolean isDone() {
        return mainTask.isDone();
    }
}

public class Zadanie6 {
    public static void main(String[] args) {
        Random  rand = new Random();

        int arr[] = new int[200];

        for(int i = 0; i < 200; i++) {
            arr[i] = rand.nextInt(100);
        }

        new MergeSortManager(arr, 10);
    
        for(int i = 0; i < arr.length; i++) {
            System.out.print(" " + arr[i]);
        }

        System.out.print("\n");
    }
}

```


## Zadanie 7
:::success
Autor Jan Dalecki	
:::
![](https://i.imgur.com/5PurHgD.png)

```java=
class OneBit implements Lock {
    private boolean[] flag;
    public OneBit (int n) {
        flag = new boolean[n]; // all initially false
    }
    public void lock() {
        int i = ThreadID.get(); // ThreadID.get() returns 0,1,..,n-1
        do {
            flag[i] = true;
            for (int j = 0; j < i; j++) {
                if (flag[j] == true) {
                    flag[i] = false;
                    while (flag[j] == true) {} // wait until flag[j] == false
                    break;
                }
            }
        } while (flag[i] == false);
        for (int j = i+1; j < n; j++) {
            while (flag[j] == true) {} // wait until flag[j] == false
        }
    }
    public void unlock() {
        flag[ThreadID.get()] = false;
    }
}
```


Deadlock może nastąpić jeżeli wszystkie procesy nie będą mogły opuścić pętli.

W algorytmie występują 3 pętle `while`. Zauważmy, że wykonanie się pętli `for` jest ograniczone przez liczbę wątków.

Indukcja po liczbie wątków $k > 0$.

$k = 1$

Zauważmy, że pętle `for` nie zostaną wykonane.
Pierwsza pętla `while` również nie zatrzyma procesu, ponieważ poniższy przebieg zawsze będzie prawdziwy:

$$
write_{p_0}(flag[0] = true) \rightarrow read_{p_0}(flag[0] \not= false)
$$

$0 < k < n$

Załóżmy, że algorytm nie zakleszcza się dla $0 < k < n$ wątków. Rozpatrzmy system w którym mamy $k = n$ wątków.

W przypadku w którym przynajmniej jeden wątek $p_i$ nie podniósł swojej flagi algorytm będzie działał jak gdyby pracowało w nim mniej niż $n$ wątków. Warunkiem pozostania w pętli `while` jest bowiem podniesiona flaga innego wątku. Zatem nie grozi ryzyko zakleszczenia jeżeli istnieje przynajmniej jeden taki proces $p_i$.

Załóżmy, że w pewnym momencie wszystkie flagi osiągnęły wartość `true`, a wątek $p_j$ jako ostatni podniósł swoją flagę. Jeżeli $j > 0$ to instnieje wątek $p_i$, $i < j$ którego flaga jest poniesiona zatem flaga $p_j$ zostanie opuszczona jeżeli tak pozostanie. Jeżeli $j = 0$ wszystkie wątki mogą czekać w ostatniej pętli. Wtedy wątek $n-1$ będzie miał pierwszeństwo wejścia do sekcji krytycznej.

## Zadanie 8
:::success
Autor: Tomasz Wołczański
:::
:::info
Poprzednie zadanie pokazało, że n współdzielonych bitów wystarczy do implementacji zamka dla n wątków. Okazuje się, że to ograniczenie jest dokładne, czyli że n współdzielonych bitów jest koniecznych do rozwiązania tego problemu. Udowodnij to twierdzenie.
:::

Pokażemy, że do implementacji zamka dla trzech wątków konieczne są 3 współdzielone bity.

Załóżmy nie wprost, że istnieje wolny od zakleszczeń algorytm, który używa dwóch bitów do rozwiązania problemu wzajemnego wykluczania dla trzech wątków. 

Pokażemy najpierw, że (1) każdy z wątków musi zmodyfikować przynajmniej jeden współdzielony bit przed wejściem do sekcji krytycznej. Załóżmy, że na początku stan obiektu zamka jest taki, jak w sytuacji, gdy w sekcji krytycznej nie znajduje się żaden wątek. Załóżmy nie wprost, że $A$ wszedł do sekcji krytycznej bez modyfikowania żadnego bitu. Może się zdarzyć tak, że przed opuszczeniem sekcji krytycznej przez $A$ będzie chciał się do niej dostać wątek $B$. Ponieważ wartości współdzielonych bitów są takie, jak w sytuacji, gdy w sekcji krytycznej nie znajduje się żaden wątek, a sam algorytm zamka jest wolny od zakleszczeń, to po pewnym czasie $B$ otrzyma dostęp do sekcji krytycznej. Tym samym w sekcji krytycznej znajdą się dwa wątki naraz, co przeczy temu, że algorytm rozwiązuje problem wzajemnego wykluczania.

Jeśli w algorytmie każdy z wątków zapisuje wartości do własnej komórki pamięci (jak np. w algorytmie piekarni), to z (1) wynika, że w implementacji są potrzebne 3 współdzielone bity, więc mamy sprzeczność. Pozostaje zatem rozpatrzyć przypadek, w którym współdzielone bity mogą być modyfikowane przez dowolny wątek. Rozważmy takie wykonanie programu, w którym wątek $B$ wchodzi do sekcji krytycznej trzykrotnie. Na podstawie (1) $B$ musi zmodyfikować przynajmniej jeden współdzielony bit przed każdym wejściem do sekcji krytycznej, a ponieważ zamek używa dwóch współdzielonych bitów, to w przynajmniej dwóch spośród trzech wejść do sekcji krytycznej jako pierwszy zostanie zmodyfikowany ten sam bit; nazwijmy go $X_B$. Niech wątek $B$ zostanie uruchomiony i zatrzymany tuż przed zmodyfikowaniem bitu $X_B$ jako pierwszego po raz pierwszy (chodzi tu o zmodyfikowanie $X_B$ przed pierwszym z tych dwóch wejść, w których $X_B$ jest modyfikowany jako pierwszy; mogłoby się zdarzyć tak, że przed pierwszym wejściem do sekcji krytycznej $X_B$ został zmodyfikowany jako drugi).
Zauważmy, że jeśli teraz uruchomiony zostanie wątek $A$, to zmodyfikuje on drugi z bitów, $X_A$. Gdyby zmodyfikował tylko $X_B$ i wszedł do sekcji krytycznej, to $B$ mógłby zostać wznowiony i nadpisałby to, co $A$ zapisał do $X_B$, więc nie byłby w stanie stwierdzić, czy jakiś wątek znajduje się w sekcji krytycznej, i mógłby wejść do niej po pewnym czasie (bo algorytm jest wolny od zakleszczeń) - w sekcji krytycznej znalazłyby się wtedy dwa wątki.
Niech $A$ zostanie teraz uruchomiony i zatrzymany tuż przed zmodyfikowaniem $X_A$. Pozwólmy działać $B$ do momentu, aż będzie on chciał zmodyfikować bit $X_B$ jako pierwszy po raz drugi. Mamy teraz następującą sytuację: $A$ chce zmodyfikować $X_A$, $B$ chce zmodyfikować $X_B$, a wartości $X_A$, $X_B$ są takie, jak w sytuacji, gdy żaden wątek nie znajduje się w sekcji krytycznej (bo $B$ wywołał przynajmniej raz metodę `unlock`, cofając zmiany, które zostały wprowadzone do $X_A$, $X_B$).

![](https://i.imgur.com/MvLfLg3.png)

To oznacza, że jeśli uruchomiony zostanie teraz wątek $C$, to po pewnym czasie wejdzie on do sekcji krytycznej (bo algorytm jest wolny od zakleszczeń). Zauważmy jednak, że jeśli po wejściu do sekcji krytycznej przez $C$ wznowimy wątki $A$,$B$, to nadpiszą one to, co $C$ zapisał do $X_A$,$X_B$ (bo $A$ chce zmodyfikować $X_A$, a $B$ chce zmodyfikować $X_B$) i nie bedą w stanie wykryć, że $C$ znajduje się w sekcji krytycznej. Ponieważ algorytm zamka jest wolny od zakleszczeń, to jeden z wątków $A$,$B$ zostanie wpuszczony do sekcji krytycznej. W szczególności może to nastąpić wtedy, gdy jeszcze znajduje się w niej $C$, co przeczy temu, że algorytm rozwiązuje problem wzajemnego wykluczania.

![](https://i.imgur.com/eSqzXUR.png)