Zrobiłem to dla zadania domowego bez pisania jakiegokolwiek asemblera. Mechanizm przełączania wątków był setjmp
/longjmp
. Wiązało się to z przydzieleniem pamięci dla każdego stosu wątku, a następnie bardzo ostrożnym masowaniem wartości w jmp_buff
, więc wykonanie przechodzi do stosu następnego wątku.
Zobacz także całkiem niezauważalny Russ Cox libtask.
Edytuj w odpowiedzi na komentarz PO: Przy podejmowaniu decyzji o zmianie wątków istnieją dwa główne kierunki: prewencyjny & spółdzielczy. W modelu wyprzedzającym, będziesz miał coś w rodzaju sygnału timera, który powoduje, że przepływ wykonania przeskoczy do głównego wątku programu rozsyłającego, który wybiera następny wątek do uruchomienia. W modelu współpracy wątki "ustępują" sobie nawzajem, albo jawnie (, np., przez wywołanie funkcji yield()
, którą podasz) lub niejawnie (, np., żądając blokady utrzymywanej przez inny wątek).
Zobacz przykładowe API libtask na przykładzie modelu kooperacyjnego, w szczególności opis funkcji taskyield()
. To wyraźny plon, o którym wspomniałem. Istnieją również nieblokujące funkcje wejścia/wyjścia, które zawierają ukrytą wydajność - bieżące "zadanie" zostaje wstrzymane do czasu zakończenia operacji wejścia/wyjścia, ale pozostałe zadania mają szansę na uruchomienie.
Przypisanie jest nieco trudne do spełnienia, ponieważ nie można tego zrobić "w C". Potrzebujesz co najmniej minimalnej ilości zespołów lub odpowiedników rozszerzeń kompilatora, aby ułatwić tworzenie nowych kontekstów wykonania i przełączać się między nimi. Albo możesz napisać własną wirtualną maszynę i implementację C, aby uruchomić na maszynie wirtualnej, ale nie sądzę, że to jest to, co twój instruktor miał na myśli ... –
Dla wielozadaniowości współpracy/wątkowania możesz znaleźć getcontext/makecontext/przydatne funkcje setcontext. – MetallicPriest
Nie widzę, jak można to zrobić bez asemblera do implementacji zapisywania/przywracania kontekstu rejestru i wskaźnika stosu. To bez nawet rozważania I/O i jak na to czekać. –