To avoid using another loop to fill in data you could do something like this:
<% index = 0 -%>
<% @products.each do |prod| -%>
<% if index%3 == 0 -%>
<tr>
<% end -%>
<td class="product">
product data goes here
</td>
<% if index%3 == 2 -%>
</tr>
<% end -%>
<% index +=1 -%>
<% end -%>
This way you'll open the tr tag if the result of the mod calculation is 0 and close the tr tag if the result is 2 (last element in the row). If you want to change the number of tds shown in a row just change the mod calculation like this:
index%x = 0 for opening the tr tag and
index%x = x-1 for closing the tr tag, where
x is the number of tds you want to display. Don't forget to add 1 to the index at the end of the loop.
Edit: MY BAD

the above code is Ruby, but it's the same for ASP. Just noticed what i was browsing

)