The OpenSSL specs constantly refer to the asynchronous mechanism you describe, but I believe I have set my BIO object to be non blocking, and I still can't get it not to block. I'm probably doing that part wrong. I create the listening BIO with BIO_set_nbio_accept(mybio, 1), and the "1" flag supposedly makes it non-blocking. But maybe when I pop it with BIO_pop and create the final client BIO object ( clientbio=BIO_pop(mybio)), it somehow loses that property. I'll have to look into that, it would be great if I could get it properly non-blocking instead of finding a work around.
I seem to remember (in the past) using the SSL_get_fd() call because I was facing a similar problem like you are describing (aka my OpenSSL file descriptor was not asynchronous/non-blocking or similar). I also felt like the OpenSSL library should "just support this" and tried for awhile to look for an "in OpenSSL library" solution to the problem - but ultimately I did not find a good solution "in library". There might be a better solution out there on a web page or similar?
I was dead set on threads, but somebody sold me on forks because then you aren't tied to a single physical cpu and the program becomes more scaleable.
The FreeBSD scheduler will schedule (any) program thread that is "ready-to-run" on to (any) available CPU/hyper-thread on your machine. So (by default) threads are not tied to a specific CPU/hyper-thread. Because... if it didn't work that way it would make your entire process "single threaded" or "time shared"

. Of course you will need to use mutex locks to keep 2x (or many) threads from stepping on each other while they are simultaineously running at the same time in your program. But.. that's the fun of programming with threads!
VERY early implementations of "thread libraries" were in fact "time shared" ! But that was a long time ago during the Clone Wars era -- during The Old Republic.
Threads (pthread(3), etc) use multiple CPUs and
hyper-threading - BUT you can actually "lock" a running thread in your program to run on a very specific CPU hyperthread using thread affinity -- which is
(very cool !) for curtain coding situtations. This means that whenever the thread is "scheduled" to run on FreeBSD the thread will always run on the CPU/hyper-thread you scheduled it for. If you have like 24 CPUs with 2 hyper-threads per/CPU or similar you can divide all the "work" you want to do amoung the available CPUs/hyper-threads. The FreeBSD way to lock a thread to a specfic CPU/hyper-thead is to use
affinity(3). Threads are very "light weight" meaning they don't take up a lot of resources when you create them and run them in your code - they are also called l
ight weight processes (LWP). You can also schedule curtain threads in your code to have a higher or lower thread priority -- which means they can be scheduled to run relatively (on your FreeBSD machine - how BUSY the machine is) more or less frequently by the FreeBSD scheduler.
fork(2) is completely fine as well - but unless you apply an "affinity" to the forked process the forked process will be automatically "scheduled" by FreeBSD on to (any) available CPU/hyper-thread when the forked/child process is "ready to run". Because the fork(2) call used to be "heavy weight" when the fork(2) call was called (aka it copied the parent processes pointers, file descriptors, and even all of the parent processes "data" into the child process) - they are generally considered to be heavy weight. They have (since) optimized fork(2) more recently so it is better and less resource intensive when it is called. On FreeBSD systems you can also consider using
vfork(2) which is MUCH more optimized when your source code is going to immediately call one of the
execve(2) system calls. (aka execvp(2), execle(2), etc).
If you want to "really" learn threads I recommend the
Orriely Ptheads book -- which seems "old" now!?

But a quick check of Amazon shows 4+ stars.
For socket processing the books by
Dr Stephen's are very good. Dr Stephens passed away many years ago, but everyone still reads his books even today to learn socket programming.
And if you want to REALLY understand FreeBSD and the *BSD's operating system in general - pickup Marshall Kirk McKusick's book -
The Design and Implementation of the 4.4 BSD Operating System. This book may seem dated - but "a large chunk" of the things you read in the book you will find in "todays" FreeBSD implementation (and the other BSD's - Ghost, NET2, etc).