# [−][src]Function rayon_core::join

    pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB) where
        A: FnOnce() -> RA + Send,
        B: FnOnce() -> RB + Send,
        RA: Send,
        RB: Send,

Takes two closures and potentially runs them in parallel. It returns a pair of the results from those closures.

Conceptually, calling `join()` is similar to spawning two threads, one executing each of the two closures. However, the implementation is quite different and incurs very low overhead. The underlying technique is called "work stealing": the Rayon runtime uses a fixed pool of worker threads and attempts to only execute code in parallel when there are idle CPUs to handle it.

When `join` is called from outside the thread pool, the calling thread will block while the closures execute in the pool. When `join` is called within the pool, the calling thread still actively participates in the thread pool. It will begin by executing closure A (on the current thread). While it is doing that, it will advertise closure B as being available for other threads to execute. Once closure A has completed, the current thread will try to execute closure B; if however closure B has been stolen, then it will look for other work while waiting for the thief to fully execute closure B. (This is the typical work-stealing strategy).

# Examples

This example uses join to perform a quick-sort (note this is not a particularly optimized implementation: if you actually want to sort for real, you should prefer the `par_sort` method offered by Rayon).

    let mut v = vec![5, 1, 8, 22, 0, 44];
    quick_sort(&mut v);
    assert_eq!(v, vec![0, 1, 5, 8, 22, 44]);

    fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
       if v.len() > 1 {
           let mid = partition(v);
           let (lo, hi) = v.split_at_mut(mid);
           rayon::join(|| quick_sort(lo),
                       || quick_sort(hi));
       }
    }

    // Partition rearranges all items `<=` to the pivot
    // item (arbitrary selected to be the last item in the slice)
    // to the first half of the slice. It then returns the
    // "dividing point" where the pivot is placed.
    fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
        let pivot = v.len() - 1;
        let mut i = 0;
        for j in 0..pivot {
            if v[j] <= v[pivot] {
                v.swap(i, j);
                i += 1;
            }
        }
        v.swap(i, pivot);
        i
    }

# Warning about blocking I/O

The assumption is that the closures given to `join()` are CPU-bound tasks that do not perform I/O or other blocking operations. If you do perform I/O, and that I/O should block (e.g., waiting for a network request), the overall performance may be poor. Moreover, if you cause one closure to be blocked waiting on another (for example, using a channel), that could lead to a deadlock.

# Panics

No matter what happens, both closures will always be executed. If a single closure panics, whether it be the first or second closure, that panic will be propagated and hence `join()` will panic with the same panic value. If both closures panic, `join()` will panic with the panic value from the first closure.

# Help

## Keyboard Shortcuts

`?`
  Show this help dialog
`S`
  Focus the search field
`↑`
  Move up in search results
`↓`
  Move down in search results
`↹`
  Switch tab
`⏎`
  Go to active search result
`+`
  Expand all sections
`-`
  Collapse all sections

## Search Tricks

Prefix searches with a type followed by a colon (e.g., `fn:`) to restrict the search to a given type.

Accepted types are: `fn`, `mod`, `struct`, `enum`, `trait`, `type`, `macro`, and `const`.

Search functions by type signature (e.g., `vec -> usize` or `* -> vec`)

Search multiple things at once by splitting your query with comma (e.g., `str,u8` or `String,struct:Vec,test`)