|
| 1 | +open System |
| 2 | +open System.Collections.Concurrent |
| 3 | +open System.Threading.Tasks |
| 4 | +//<snippet1> |
| 5 | +// Demonstrates: |
| 6 | +// ConcurrentDictionary<TKey, TValue> ctor(concurrencyLevel, initialCapacity) |
| 7 | +// ConcurrentDictionary<TKey, TValue>[TKey] |
| 8 | + |
| 9 | +// We know how many items we want to insert into the ConcurrentDictionary. |
| 10 | +// So set the initial capacity to some prime number above that, to ensure that |
| 11 | +// the ConcurrentDictionary does not need to be resized while initializing it. |
| 12 | +let NUMITEMS = 64 |
| 13 | +let initialCapacity = 101 |
| 14 | + |
| 15 | +// The higher the concurrencyLevel, the higher the theoretical number of operations |
| 16 | +// that could be performed concurrently on the ConcurrentDictionary. However, global |
| 17 | +// operations like resizing the dictionary take longer as the concurrencyLevel rises. |
| 18 | +// For the purposes of this example, we'll compromise at numCores * 2. |
| 19 | +let numProcs = Environment.ProcessorCount |
| 20 | +let concurrencyLevel = numProcs * 2 |
| 21 | + |
| 22 | +// Construct the dictionary with the desired concurrencyLevel and initialCapacity |
| 23 | +let cd = ConcurrentDictionary<int, int>(concurrencyLevel, initialCapacity) |
| 24 | + |
| 25 | +// Initialize the dictionary |
| 26 | +for i = 0 to NUMITEMS - 1 do |
| 27 | + cd[i] <- i * i |
| 28 | + |
| 29 | +printfn $"The square of 23 is {cd[23]} (should be {23 * 23})" |
| 30 | +//</snippet1> |
| 31 | + |
| 32 | +do |
| 33 | + //<snippet2> |
| 34 | + // Demonstrates: |
| 35 | + // ConcurrentDictionary<TKey, TValue>.TryAdd() |
| 36 | + // ConcurrentDictionary<TKey, TValue>.TryUpdate() |
| 37 | + // ConcurrentDictionary<TKey, TValue>.TryRemove() |
| 38 | + |
| 39 | + let mutable numFailures = 0 // for bookkeeping |
| 40 | + |
| 41 | + // Construct an empty dictionary |
| 42 | + let cd = ConcurrentDictionary<int, string>() |
| 43 | + |
| 44 | + // This should work |
| 45 | + if cd.TryAdd(1, "one") |> not then |
| 46 | + printfn "CD.TryAdd() failed when it should have succeeded" |
| 47 | + numFailures <- numFailures + 1 |
| 48 | + |
| 49 | + // This shouldn't work -- key 1 is already in use |
| 50 | + if cd.TryAdd(1, "uno") then |
| 51 | + printfn "CD.TryAdd() succeeded when it should have failed" |
| 52 | + numFailures <- numFailures + 1 |
| 53 | + |
| 54 | + // Now change the value for key 1 from "one" to "uno" -- should work |
| 55 | + if cd.TryUpdate(1, "uno", "one") |> not then |
| 56 | + printfn "CD.TryUpdate() failed when it should have succeeded" |
| 57 | + numFailures <- numFailures + 1 |
| 58 | + |
| 59 | + // Try to change the value for key 1 from "eine" to "one" |
| 60 | + // -- this shouldn't work, because the current value isn't "eine" |
| 61 | + if cd.TryUpdate(1, "one", "eine") then |
| 62 | + printfn "CD.TryUpdate() succeeded when it should have failed" |
| 63 | + numFailures <- numFailures + 1 |
| 64 | + |
| 65 | + // Remove key/value for key 1. Should work. |
| 66 | + let mutable value1 = "" |
| 67 | + |
| 68 | + if cd.TryRemove(1, &value1) |> not then |
| 69 | + printfn "CD.TryRemove() failed when it should have succeeded" |
| 70 | + numFailures <- numFailures + 1 |
| 71 | + |
| 72 | + // Remove key/value for key 1. Shouldn't work, because I already removed it |
| 73 | + let mutable value2 = "" |
| 74 | + |
| 75 | + if cd.TryRemove(1, &value2) then |
| 76 | + printfn "CD.TryRemove() succeeded when it should have failed" |
| 77 | + numFailures <- numFailures + 1 |
| 78 | + |
| 79 | + // If nothing went wrong, say so |
| 80 | + if numFailures = 0 then |
| 81 | + printfn " OK!" |
| 82 | +//</snippet2> |
| 83 | + |
| 84 | +do |
| 85 | + //<snippet3> |
| 86 | + // Demonstrates: |
| 87 | + // ConcurrentDictionary<TKey, TValue>.AddOrUpdate() |
| 88 | + // ConcurrentDictionary<TKey, TValue>.GetOrAdd() |
| 89 | + // ConcurrentDictionary<TKey, TValue>[] |
| 90 | + |
| 91 | + // Construct a ConcurrentDictionary |
| 92 | + let cd = ConcurrentDictionary<int, int>() |
| 93 | + |
| 94 | + // Bombard the ConcurrentDictionary with 10000 competing AddOrUpdates |
| 95 | + Parallel.For( |
| 96 | + 0, |
| 97 | + 10000, |
| 98 | + fun i -> |
| 99 | + |
| 100 | + // Initial call will set cd[1] = 1. |
| 101 | + // Ensuing calls will set cd[1] = cd[1] + 1 |
| 102 | + cd.AddOrUpdate(1, 1, (fun key oldValue -> oldValue + 1)) |> ignore |
| 103 | + ) |
| 104 | + |> ignore |
| 105 | + |
| 106 | + printfn $"After 10000 AddOrUpdates, cd[1] = {cd[1]}, should be 10000" |
| 107 | + |
| 108 | + // Should return 100, as key 2 is not yet in the dictionary |
| 109 | + let value = cd.GetOrAdd(2, (fun key -> 100)) |
| 110 | + printfn $"After initial GetOrAdd, cd[2] = {value} (should be 100)" |
| 111 | + |
| 112 | + // Should return 100, as key 2 is already set to that value2 |
| 113 | + let value2 = cd.GetOrAdd(2, 10000) |
| 114 | + printfn $"After second GetOrAdd, cd[2] = {value2} (should be 100)" |
| 115 | +//</snippet3> |
0 commit comments